Here it is: the ggplot advent calendaR! The “gg” in ggplot refers to
the grammar of graphics. For the next 25 days, we will go through an
introduction to the grammar of graphics, make a lot of visualizations
(some good, some bad), and learn some of the basic functions and
features of the ggplot2 package. NOTE I am no expert in
ggplot. I literally learned while creating this tutorial, and that was a
big motivation in doing this. If there are errors or smoother ways to do
the same thing, please let me know! R is about constant learning and
improving, ggplot is no different.
DAY 1
On the first day of Christmas… we’re jumping into the tidyverse!
ggplot is part of the tidyverse, a group of packages that also
includes dplyr, readr, and other very helpful packages that you should
have! You can install and load ggplot separately, but… why? (:
The package we’re using is actually called ggplot2. Super-duper fun
fact: “ggplot2 is called ggplot2 because once upon a time there was just
a library ggplot. However, the developer noticed that it used an
inefficient set of functions. In order for not to break the API, the
authors introduced a successor package ggplot2. However, the central
function in this package is still called ggplot(), not ggplot2()!”
(wasn’t that fun? Source: Freeman & Ross, 2019).
Install and load tidyverse:
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ───────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.2 ──✔ ggplot2 3.3.6 ✔ purrr 0.3.4
✔ tibble 3.1.8 ✔ dplyr 1.0.10
✔ tidyr 1.2.1 ✔ stringr 1.4.1
✔ readr 2.1.2 ✔ forcats 0.5.2 ── Conflicts ──────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ ggplot2::annotate() masks ctmm::annotate()
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
Let’s also load our data using another tidyverse package, readr, and
then view the data. We will be working with two datasets throughout this
tutorial:
Using readr and read_csv(), instead of read.csv() from base R, loads
our data in as tibbles. Why use tibbles over traditional dataframes?
Three reasons: (1) the input types aren’t automatically changed when you
read in the data, (2) you can keep lists as columns, and (3) you can use
non-standard variable names (e.g., starting with a number, as in
“1st_place”). Thank you to my friend Pat for this explanation!
DAY 2
On the second day of Christmas…
…we learned the language of ggplot aka the Grammar of Graphics!
ggplot is different from base R graphics. How? Base R graphics work on
the individual vectors, ggplot works on dataframes (Source: Prabhakaran,
2017). ggplot works by adding layer upon layer to create your
visualization. In base R graphics, you put all the information in one
code and it spits out a graphic. In ggplot, you build your grahpic with
layers.
What are some of the different components of a graphic (Source:
Freeman & Ross, 2019)? - data - geometric objects (geoms) -
aesthetics - statistical transformations - position adjustments - scale
- coordinate system - facets - themes
The first layer of ggplot is always…ggplot. If you run the code
below, you will get a blank graphic with a grey background. The grey
background is the ggplot default. In base R, you can’t run just
function, e.g., boxplot(), without a vector in the brackets.

Now let’s add another layer to ggplot. We’ll specify the dataset and
what we want our x and y axes to be.

When you run this code, you should see that we now have axes and
labels on top of our grey background. Note, you can drop the x= and y=
and the code will run the same (don’t take my word for it, try it
yourself!).
DAY 3
On the third day of Christmas…
…we added data to our graphic and explored geom layers!
Geoms, or geometric objects, are graphical representations of the
data. There are many many types of geoms (here’s a long list of
examples: https://ggplot2.tidyverse.org/reference/#geoms). Let’s
try a few.
Geom layers start with “geom_” and are followed by the type of
geometric object, e.g., “geom_point” or “geom_line”. Because we’re
adding a new layer onto our graphic, we use a + after our first line of
code. To keep things tidy and easy to read, I usually start my new layer
on a new line.

Since we are working with categorical data (types of trees), the
points all fall on three lines. Let’s change our x and y axes before
moving on to a different type of geom.

Now we’re comparing two continuous variables (xmas magic and tree
height) so the points are scattered across our graphic.
Let’s look at some different geom types. We’ll go back to type
vs. height for this one.

When creating graphics, always consider the type of data you’re
working with (e.g., continuous vs. discrete). The type of data you’re
working with should determine the type of geom you choose. Some geoms
won’t run properly if the type of data you’re inputting doesn’t work
with those data types.
One more example of a geom layer using the trees data.

DAY 4
On the fourth day of Christmas…
…we introduced pipes!
This is a bit of a segue from ggplot and you certainly don’t need to
use pipes to create beautiful ggplot graphics BUT it might make your
data look more tidy.
The pipe operator (%>%) is included in the tidyverse, so if you
loaded the tidyverse, then you’ve got access to %>%. From R for Data
Science, “Pipes are a powerful tool for clearly expressing a sequence of
multiple operations…The point of the pipe is to help you write code in a
way that is easier to read and understand.” In words, %>% means “and
then”. By using a pipe, you’re telling R to run the first line of code
AND THEN run the next line of code.
Why do I bring this up now? Previously, we ran this code to produce a
boxplot showing tree heights:

Often, in online examples and tutorials, you will see ggplot codes
written with the dataframe listed first, then a pipe leading into the
ggplot code. If you run the code below, you’ll see that we can produce
the exact same graphic:

Another tip from my friend Pat, “The most useful application of the
pipe is to plug the result of one function into another without creating
intermediate data frames” Here’s an example of what this might look
like:
my_data %>% function1(arguments, etc) %>% function2(arguments,
etc)
DAY 5
On the fifth day of Christmas…
…we started working with the aesthetics of our graphic!
Aesthetic mapping describes the visual properties of the graphic. We
can change the aesthetics of ggplot() or each layer we add to it. We use
aes() and then customize our graphic to appear how we’d like it. On Day
3, we created some pretty boring graphs: white boxes or violins, black
dots, and grey backgrounds. While this is fine for exploring data, it’s
perhaps not how we want our finished product to look!
Today, we’re going to work on changing the colours of our data. Let’s
start with the scatterplot, i.e., the geom_point() graphic.
To change the aesthetics of the geom_point() dots, we add “aes()”
within the parentheses of geom_point(). You may have noticed that we
already used “aes()” when we told ggplot what we wanted our X and Y axes
to be. Now we will also tell ggplot to assign different colours to our
points by tree type. In this case, we are not choosing the colours.

Now instead of changing the aesthetics of the ggplot() layer, let’s
instead change the aesthetics of the geom_point() layer.

Looks the same right? So what’s the difference? Right now, nothing
appears different, but later it may impact how your graphic looks. When
you change the aesthetics of the ggplot() layer, those aesthetics will
be applied as the default to all of your subsequent layers. If you
change the aesthetics of an individual layer, it will only be applied to
that layer and it will override the default.
What if we want to use all the same colour for our points? Find out
tomorrow!
DAY 6
On the sixth day of Christmas…
…we continued with aesthetic mapping and colours!
So you want all your points to be one colour. Sounds easy-peasy
right? Not quite.
Here’s why it’s a bit confusing. I had to do a bit of digging to
understand exactly why this is…
When we want to map a variable of our data (e.g., telling ggplot we
want to colour by tree type), we put aes() inside the geom_point(). If
we want to apply a constant colour (constant value) to our points (e.g.,
telling ggplot we want all our points to be blue), we put aes() OUTSIDE
geom_point). Try both the codes below to see what happens.

Note: in this case you could also leave out the aes() within
geom_point() and it would run the same.
For more of an explanation on why aes() works this way, you can check
out this helpful thread on stackoverflow that helped me: https://stackoverflow.com/questions/41863049/when-does-the-argument-go-inside-or-outside-aes.
Here’s another really fantastic resource: https://drive.google.com/file/d/1Dvul1p6TYH6gWJzZRwpE0YX1dO0hDF-b/view.
DAY 7
On the seventh day of Christmas…
…we changed the colours of boxplots!
Try running the same code from yesterday but on a geom_boxplot()
graphic.

Hmm… okay. But what if we wanted the inside of the boxes to be
coloured, not the outline?
Instead of “colour =” we use “fill =” instead.

This works for points too. The default for geom_point() are solid
points, but you can change these to points with different outline and
fill colours.

We can also change the colours of our boxplots by tree type, as we
did with the points on Day 5. Remember from yesterday that because we
want to change the colours by a variable (i.e., multiple colours
determind by the levels of the variable) rather than change using a
constant value (i.e., single colour), we put this INSIDE aes().

Now that we’re playing around with colours. It’s time I introduce you
to a good colour resource: http://www.stat.columbia.edu/~tzheng/files/Rcolor.pdf.
There are many great online resources about colours, but I like this one
made by Dr. Ying Wei.
DAY 8
On the eight day of Christmas…
…we finally took a break from talking about colours!
Colour is a big one, but let’s work with some other changes to the
aesthetics of our graphic. Let’s bring back the geom_point() graphic.
I’m using the theme of Christmas, but of course, December has many, many
holidays. Hanukkah starts on Dec. 17 this year, so let’s make a
Hanukkah-inspired graphic and look at different ways to change the
aesthetics of our graphs.

Here we’ve specified the shape, the colour, the size, and the stroke
(line thickness) of the points. There are many changes we can make to
aesthetics, these are just a few examples. Let’s take a look at a line
graph and how we can customize that.
We haven’t made a line graph yet, so let’s create a simple one first,
before we customize it.

Here we’ve specified that we want each line to represent a different
tree type by using “group=”. This goes inside aes() because it is being
applied to the data (i.e., it can’t be done without this specific
dataset)
Now let’s spruce it up. (“spruce” it up… because they’re trees…
hahahaha. A little Christmas cheer for you).

What if we want to show the points AND the lines? Tomorrow we will
add another layer to our graphic.
DAY 9
On the ninth day of Christmas…
… we added another geom_ layer to our graphic.
Here’s our geom_lin() graphic from yesterday:

Now let’s add points to it as well. It’s as simple as +
geom_point()!

And if you want to change the aesthetics of those points, where do
you do it? Within geom_point()!

They kind of look like Christmas lights :)
DAY 10
On the tenth day of Christmas…
…we edited the text of our graphics.
Editing the look of your text and fonts in ggplot is easy, with lots
of options to make it look exactly how you want it. Here’s our graphic
from yesterday.

What kind of things might we want to change? Maybe we want a title
and more informative and cleaner-looking axis labels. To do this, we
need to add new layer, labs() for labels. You can also use xlab(),
ylab(), and ggtitle() to add them individually.

What if we want to make more aesthetic changes to our fonts and
labels? We will need to use themes!
DAY 11
On the eleventh day of Christmas…
…we introduced themes!
We can use themes to make finer adjustments to non-data parts of our
graphic. While labs() is fine for adding labels and a title, themes
allow us to choose the size, font, colour, position, etc. of that text.
Today I’m going to introduce you to some complete themes.
Let’s go back to our nice, clean-looking boxplots:

While the grey backgronud is the default in ggplot, it’s certainly
not a requirement. That’s often one of the first things I change!

Theme_bw() is a complete theme, meaning that it’s a theme that can be
applied as a layer that changes the look of your overall plot.
Theme_gre() is the default. You can also try other complete themes, such
as theme_dark(), theme_light(), theme_classic(), theme_void() and
more.

Personally, I like this one best!
DAY 12
On the twelfth day of Christmas…
… we worked with theme() and more control with non-data parts of our
graphics!
Here, I’m keeping theme_classic() and building on top of that.

In this case, I wanted to show you how you can keep the grid lines,
while changing to theme_classic(). We’ve also changed the style and size
of the title, moved the legend and changed the colours, changed the
colour of the y axis label, and changed the size of the x axis tick
labels. This is by no means a pretty graphic, but hopefully it gives you
an idea of different ways we can change features of our graphic. There
are many more changes you could make and lots of great online tutorials
that cover this in more detail.
DAY 13
On the thirteenth day of Christmas…
… we introduced scales!
To do this, we need to introduce scales. We’ll be working with scales
for a few days - get excited! Scales allow us to override defaults.
Similar to themes, scales allow us more control over what our graphic
looks like, but scales focus on changing the look of the data.
Let’s start with position scales. The most commonly used are
scale_x_continuous() and scale_y_continuous(). Since we are working with
categorical data right now (tree types), we could swap out
scale_x_continuous() for scale_x_discrete(). Using these, we can set the
limits of our scales. We don’t need to change the limits of our discrete
(x) axis, but let’s change the limits of our y axis.
Here’s our boxplot graphic, but I’ve changed our y axis to Christmas
magic instead of tree height:
trees %>%
ggplot(aes(x=type, y=xmas.magic))+
geom_boxplot(aes(fill=type), colour="black")+
labs(title="Christmas Trees", x=NULL, y="Christmas magic")+
theme_classic()
Now let’s try changing the y-axis limits:

Note: you can also use lims(x=c(#,#), y=c(#,#)) (replacing the #s
with your desired limits). This is simpler and faster, but we will stick
with scale_y_continuous() because we will make additional adjustments
below.
When we increased our y-axis limits, it changes the labels of our
y-axis ticks to include 0.5s. Maybe we’d rather have whole numbers or
maybe just fewer numbers altogether. We can specify this using the same
scale_y_continuous() but adding “breaks=”.

If we don’t want any breaks we can specify by using
“breaks=NULL”.
Finally, we can modify the axis tick labels. Let’s change the names
of our x-axis tick labels.

There’s much much more that can be done with position scales. I
suggest taking a look at Ch. 10 of this book by Hadley Wickham, Danielle
Navarro, and Thomas Lin Pedersen, which I relied on heavily for this
section. https://ggplot2-book.org/scale-position.html#scale-position
DAY 14
On the fourteenth day of Christmas…
… we worked with colour scales!
We’ve been seeing the same three colours over and over: red, green,
blue. But what if we want to specify the colours when we choose colour
or fill by type? This is where colour scales come in.
We can choose a different colour palettes by installing colour
palette packages and loading them.
Here is a good resource, RColorBrewer.
install.packages("RColorBrewer")
library(RColorBrewer)
Let’s choose a palette from RColorBrewer. You can find the names and
palettes here: https://r-graph-gallery.com/38-rcolorbrewers-palettes.html
or you can run this code to display them in R:

Here we’ll create our boxplots again, so we will want to change the
“fill” rather than the “colour”, therefore we use scale_fill_brewer().
We’re going to try the Dark2 palette:

What if you like the palette but don’t like how ggplot applied the
colours? Or maybe you can’t find the perfect palette and want to create
your own? We can manually assign colours too… but we’ll wait until
tomorrow for that one!
DAY 15
On the fifteenth day of Christmas…
…we learned how to assign colours manually!
For this we use scale_color_manual() and/or scale_fill_manual(). For
the boxplots, we will use scale_fill_manual().

The colours will be assigned in the order we gave them, so you can
also repeat a colour (e.g., green, red, green) and it will be assigned
in that order. Instead of colour names, you can also use the color codes
(e.g., #E69F00).
We’ll be using these colours again and again, so why don’t we save
them as a vector?
xmas <-c("darkgreen", "firebrick2", "mediumseagreen")
You can also specific the colours that will be assigned to each level
of your variable:

Christmas colours aren’t necessarily colour-blind friendly. There are
lots of fantastic resources when considering colour-blind friendly
palettes for your graphics. Here’s one: https://colorbrewer2.org/#type=sequential&scheme=YlGnBu&n=9
DAY 16
On the sixteenth day of Christmas…
…we worked with a different type of plot and added multiple geoms to
one plot!
We haven’t even touched the sleigh dataset! Let’s create a graphic
with that so we can work with a different type of plot.

Here we’ve told ggplot to colour each sleigh type a different colour
(some are nearly impossible to differentiate but we won’t worry about
that today).
Maybe we want to add a trendline to our plot. We can do this by
adding a geom_smooth() layer:

And maybe we want to customize that line. The default is a blue line
with grey background. We can change that to a black line using “colour
=” and we can change the transparency of the error using “alpha =”. We
can also change the method of how the line is calculated using “method
=”

Alpha can also be helpful when you have overlapping points.

More detailed and additional information on colour scales can be
found here: https://ggplot2-book.org/scale-colour.html
DAY 17
On the seventeenth day of Christmas…
…we edited our legend!
Want to move your legend? Change the shape, style, size? Get rid of
it completely?
Let’s go back to our boxplots.We can remove the legend using
“show.legend = FALSE”. We put this in the geom_boxplot() since the
legend is linked to that layer.

Another option is to use guides(). In this case we use fill=“none”
but if we were working with colour instead of fill, we would type
colour=“none”. This way of removing the legend is handy in instances
when you have multiple geoms. We can add one little line of code to
remove the legend, instead of typing “show.legend=F” into each geom
layer.

We can also change the text of the legend. When we changed the x-axis
labels, the legend didn’t change with it. Let’s fix that now. We do this
by adding the same “labels = c(…” in scale_fill_manual() as well as
scale_x_discrete(). Why? Because scale_fill_manual() refers to the
colours of your data, and the legend represents that (they are directly
linked). scale_x_discrete() is focused solely on the x-axis.

REFRESHER What if we want to change the position of the
legend or the colour of the text?? Remember back to when we talked about
themes? If we want to make changes to anything that’s not related to the
data (i.e., it could be a plot of anything or one without any data in
it), we use THEMES.

DAY 18
On the eighteenth day of ChRistmas…
… we learned about guides!
Yesterday, we made some edits to our legend using scales and themes.
Today, we will introduce one more way to exercise more fine control over
your graphics: guides() and guide_ functions! Guides, like our legends
and axes, help us or our audience interpret our plots. We can use
guides() or the guide_ argument _*() functions to make additional
changes to our legends and axes. Here’s a great explanation of scales
and guides from Ch.15 of Wickham, Navarro and Pederson’s book, which I
highly recommend you check out: https://ggplot2-book.org/index.html
“Formally, each scale is a function from a region in data space (the
domain of the scale) to a region in aesthetic space (the range of the
scale). The axis or legend is the inverse function, known as the guide:
it allows you to convert visual properties back to data. You might find
it surprising that axes and legends are the same type of thing, but
while they look very different they have the same purpose: to allow you
to read observations from the plot and map them back to their original
values.”
Let’s look at some examples. Let’s try a new graphic with our data,
and we’ll use a gradient colour scale to colour our points based on the
amount of “Christmas magic” in our trees:

Before we get back to guides, let’s quikcly chat about the gradient
scale. There are many, many ways you can edit the colours, but in this
case we told ggplot that we wanted to change the colour of our points
with a gradient “scale_colour_continuous()” and then we set the high and
low colours. We could have also set the middle colour or chosen an
existing gradient. Learn more here: https://ggplot2-book.org/scale-colour.html
Back to guides! (colours are just so distracting!!)
We can make additional edits to our legends using “+ guides()” or by
specifying the “guide =” argument within our scale layer
(scale_colour_continuous(), which corresponds with our legend).

Here we’ve flipped our bar horizontally and increased the size of the
legend. No changes have been made to the rest of our graphic. We can
achieve the exact same output by adding “guide =” to our
scale_colour_continuous() layer.
trees %>%
ggplot(aes(x=needle.drop, y=height))+
geom_point(aes(colour=xmas.magic), size=2)+
theme_classic()+
scale_colour_continuous(low="red", high="mediumseagreen", guide = guide_colourbar(reverse=TRUE, direction = "horizontal", barheight=unit(2, "cm")))

Here are a couple more ways we can use guides to edit our legend.
Let’s change up our plot a bit.

There’s a bit of overlap in our points, so let’s adjust the
transparency using alpha:

But maybe we don’t want our legend to also have transparent points,
so we can use a guide to override this aesthetic change.

Finally, let’s use guides to change the aesthetics of our axes. We
will go back to our sleighs dataset for this one.
sleighs %>%
ggplot(aes(x=name, y=deerpower))+
geom_point()+
labs(x="Sleighs")+
theme_classic()

As you can see, the names of the sleighs are impossible to read.
Let’s flip the labels at the bottom so that they run vertically
instead.
sleighs %>%
ggplot(aes(x=name, y=deerpower))+
geom_point()+
theme_classic()+
labs(x="Sleighs")+
guides(x=guide_axis(angle=90))

Much better! Hopefully now you have an idea of some of the ways you
can edit your guides (legends, axes) using guides() and guide =.
Day 19
On the nineteenth day of Christmas…
… we made position adjustments!
Position adjustments are handy if you have overlapping geoms or data.
You can override the default using the position argument in the geom_()
functiions.
Instead of boxplots, let’s look at the raw data points using a
different type of geom later, geom_jitter().

Position adjustments come in handy with point data like this, more so
when we’re working with large datasets that have many points. Let’s
adjust the position of our jittered points.

Let’s look at another example. We’ll bring back our scatterplot of
sleigh data but I’m going to cut it down a bit to make it a easier to
work with. I’ll do this using another tidyverse package, dplyr.

That legend is fine, but let’s get rid of it and instead label each
point.Do you remember how to remove the legend?
sleighs.subset %>%
ggplot(aes(x=deerpower, y=km_per_carrot))+
geom_point(aes(fill=name), shape=21, size=3, show.legend = F)+
geom_text(aes(label=name))+
theme_classic()
Well this might work better, but the labels are all overlapping and
difficult to read. This is where position_nudge() comes in handy!

And because our Stealth Sleigh is off the plot, let’s fix that using
what we learned on Day 13 about limits.

DAY 20
On the twentieth day of Christmas…
… we did some position adjustments with bar plots!
First, let’s create a barplot since we haven’t done that yet. We’ll
base it on our previous sleighs subset.
sleighs.subset %>%
ggplot(aes(x=bells, y=reins))+
geom_col()+
scale_x_discrete(limits=c(4, 6, 8))+
theme_classic()
Warning: Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

The sleighs come with 4, 6, or 8 bells. So here we’re displaying the
counts of sleighs in each category (# of bells). But maybe we want to
see some additional information in our barplot, such as the number of
reins on the sleigh. I’ve heard that Santa takes these things super
seriously, so this is completely practical and reasonable plot. Note
that we have to specify that reins is a categorical variable, not a
continuous one, using as.character(). In this case, we can’t have 3.5
reins.
sleighs.subset %>%
ggplot(aes(x=bells, y=reins, fill=as.character(reins)))+
geom_col()+
scale_x_discrete(limits=c(4, 6, 8))+
theme_classic()+
scale_fill_manual(values=xmas)
Warning: Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

The default is a stacked barplot (position = “stack”), but there are
other ways we could display this using position adjustments. This option
shows it as a percent using position = “fill”.
sleighs.subset %>%
ggplot(aes(x=bells, y=reins, fill=as.character(reins)))+
geom_col(position="fill")+
scale_x_discrete(limits=c(4, 6, 8))+
theme_classic()+
scale_fill_manual(values=xmas)
Warning: Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

We can also position them side by side using position = “dodge”. Note
that the red bar on the left and the green bar on the right are two bars
side by side.
sleighs.subset %>%
ggplot(aes(x=bells, y=reins, fill=as.character(reins)))+
geom_col(position="dodge")+
scale_x_discrete(limits=c(4, 6, 8))+
theme_classic()+
scale_fill_manual(values=xmas)
Warning: Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

Want a review? Try changing the name of your legend.
DAY 21
On the twenty-first day of Christmas…
…we learned about faceting!
Faceting produces smaller graphs that can be displayed alongside one
another. We use facet_wrap() and facet_grid() for this.
Let’s start with facet_wrap(). Remember our line graph that looks a
bit like Christmas lights? Let’s use that. Here it is, as a
reminder:
xmas
Error: object 'xmas' not found
Now, instead of having all three lines on one plot, let’s create
three smaller plots and display them together.

Let’s do the same thing but using facet_grid(). The syntax is a
little different, but we’ve produced the exact same set of plots. In our
case, “.~type” puts the plots side by side.
trees %>%
ggplot(aes(x=height, y=xmas.magic, group=type))+
geom_line(linetype="dashed")+
geom_point(size = 3, shape = 8)+
facet_grid(.~type)+
guides(colour=FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

If we want to stack our plots instead, we change up the coding within
facet_grid(). In our case, “type~.” stacks the plots.
trees %>%
ggplot(aes(x=height, y=xmas.magic, group=type))+
geom_line(linetype="dashed")+
geom_point(size = 3, shape = 8)+
facet_grid(type~.)+
guides(colour=FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

Our datasets aren’t really set up for this type of grid, but let’s
look at plots of reins by bells to show you how you could set up
facet_grid() with multiple plots. A plot area is produced with two
levels for reins and three levels for bells.

You can also use “scales =” to adjust the scales of all or each of
the plots. Let’s go back to our first set of plots from today:
trees %>%
ggplot(aes(x=height, y=xmas.magic, group=type))+
geom_line(linetype="dashed")+
geom_point(size = 3, shape = 8)+
facet_wrap(~type, ncol=3)+
guides(colour=FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

To adjust the scales, we add “scales =” to facet_wrap().
trees %>%
ggplot(aes(x=height, y=xmas.magic, group=type))+
geom_line(linetype="dashed")+
geom_point(size = 3, shape = 8)+
facet_wrap(~type, ncol=3, scales = "free_y")+
guides(colour=FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

By choosing “free_y” we have changed the y scale from fixed to free
but the x-axis remained the same. Take a look at how the y-axis scale is
now different for each plot. Other options are “free_x”, “fixed”, or
“free”.
There are lots more things you can do with facets. Check out Ch. 17
of “ggplot2:Elegant Graphics for Data Analysis” for more: https://ggplot2-book.org/facet.html
Want a review? Try changing the style of the points and lines in
these plots. Also, try removing the grey background!
DAY 22
On the twenty-second day of Christmas…
…we annotated our plots!
Sometimes we may want to add text to our plots, not just titles and
labels, but annotations on the data or plot area. We can do this using
geom_text(). We did this on Day 19 when we talked about position
adjustments. Now we’re going to discuss annotations in more detail.
Let’s bring back that graphic, but with some added text and labels.

Let’s say that the elf in charge wants to send this to Santa but
wants to mark, on the plot, which sleigh is her top choice for Santa. We
can do this using the annotate() function. We first need to set up our x
& y ranges and our caption text.
yrng <- range(sleighs.subset$km_per_carrot)
xrng <- range(sleighs.subset$deerpower)
caption <- paste(strwrap("Sprinkle's top choice: Winter Express"))
Then we can make our plot:

Or may we want to annotate the points directly to show Santa which
ones Sprinkle chose as her top recommendations.

Hopefully it’s clear which point we’re referring to here (“Winter
Express”) but in case it’s not, we can also highlight it by adding
another geom_point() layer. Note that we add additional geom_point()
layers but they must go before our original geom_point layer or the
orange dots will appear on top of the coloured points (which in this
case would also be fine!).

There’s much more you can do with annotations. As usual, I’ll direct
you to ggplot2: Elegant Graphics for Data Analysis: https://ggplot2-book.org/annotations.html#direct-labelling
Want a review? Try changing the colour palette of the points in this
plot.
DAY 23
On the twenty-third day of Christmas…
…we introduced cowplot!!
“Cowplot????????:
Yes. Cowplot.
Cowplot is an add-on to ggplot and allows us to combine several plots
into one.
“How is that different from faceting??”
Cowplot allows you to combine plots of different types into one
image!
First, let’s install and load the cowplot package.
install.packages("cowplot")
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.2/cowplot_1.1.1.tgz'
Content type 'application/x-gzip' length 1373845 bytes (1.3 MB)
==================================================
downloaded 1.3 MB
The downloaded binary packages are in
/var/folders/xp/d3s384tn7f16chkdxc5n6jh80000gn/T//RtmpnNy2R1/downloaded_packages
Now let’s make a few graphs.
sleigh.plot1 <-sleighs.subset %>%
ggplot(aes(x=km_per_carrot, y=bag_space))+
geom_point(colour="darkgreen")+
theme_classic()
sleigh.plot2 <-sleighs.subset %>%
ggplot(aes(x=bells))+
geom_bar(fill="firebrick2")+
theme_classic()
sleigh.plot3 <-sleighs.subset %>%
ggplot(aes(x=km_per_carrot, y=deerpower))+
geom_quantile(colour="gold3")+
theme_classic()
tree.plot1<-trees %>%
ggplot(aes(x=type, y=height))+
geom_boxplot(fill="seagreen")+
theme_classic()
tree.plot2 <-trees %>%
ggplot(aes(x=xmas.magic))+
geom_dotplot(fill="tomato3")+
theme_classic()
Now we can use cowplot to create one image with multiple plots.
plot_grid(tree.plot1, tree.plot2, nrow=1, labels = c("A", "B"))
Bin width defaults to 1/30 of the range of the data. Pick better value with `binwidth`.

Let’s combine all 5 plots into one image.
plot_grid(tree.plot1, tree.plot2, sleigh.plot1, sleigh.plot2, sleigh.plot3, nrow = 2)
Bin width defaults to 1/30 of the range of the data. Pick better value with `binwidth`.
Smoothing formula not specified. Using: y ~ x

This looks okay, but maybe we wanted the tree plots to be on the top
and the sleigh plots to be on the bottom. We can do that but specifying
which ones go in the top row and which on the bottom.

Want a review? Try changing your axis labels to something cleaner and
more informative (pretend you were going to publish this image in a
paper!). Hint: you will need to edit the labels in your original plots,
not in the plot_grid(). If you can’t remember how, check out day 10.
DAY 24
On the twenty-fourth day of Christmas…
…we put our knowledge to the test!
Today we will not learn anything new. Instead, we will bring together
what we’ve learned to build some plots!
We will build three different plots:
Build a plot using the trees dataset that shows violin plots by
tree type, also coloured by tree type. Add a title, informative axis
labels, and a legend at the bottom.
Build a plot using the sleighs dataset that shows deerpower by
weight, with a trendline, points coloured by km per carrot (with a size
and shape where you can see the colour differences), and an informative
title, axis labels, and legend.
Build an image that shows the previous two graphs side-by-side in
one image with labels A and B.
And as a little Christmas Eve gift, here is a fantastic cheatsheet
for ggplot2. I keep it in my bookmarks bar :) https://github.com/rstudio/cheatsheets/blob/main/data-visualization-2.1.pdf
- this might come in handy while you are working through these
exercises!
DAY 25 - MERRY CHRISTMAS!!!
I hope you enjoyed this advent calendar. Similarly to the previous
one, for day 25, I’ve given you code for a Christmas visual created by
someone else (in this case, data scientist, Jodie Burchell). But unlike
in the original R advent calendaR, now you should be able to understand
a lot of the components and the grammar used in this code. Load the data
and run the code to see what happens! The original blog post can be
found here: https://t-redactyl.io/blog/2016/12/a-very-ggplot2-christmas.html
First, load in this dataset, which is available through git hub.
ChristmasTree <- read.csv("https://raw.githubusercontent.com/t-redactyl/Blog-posts/master/Christmas%20tree%20base%20data.csv")
# Generate the "lights"
Desired.Lights <- 50
Total.Lights <- sum(round(Desired.Lights * 0.35) + round(Desired.Lights * 0.20) +
round(Desired.Lights * 0.17) + round(Desired.Lights * 0.13) +
round(Desired.Lights * 0.10) + round(Desired.Lights * 0.05))
Lights <- data.frame(Lights.X = c(round(runif(round(Desired.Lights * 0.35), 4, 18), 0),
round(runif(round(Desired.Lights * 0.20), 5, 17), 0),
round(runif(round(Desired.Lights * 0.17), 6, 16), 0),
round(runif(round(Desired.Lights * 0.13), 7, 15), 0),
round(runif(round(Desired.Lights * 0.10), 8, 14), 0),
round(runif(round(Desired.Lights * 0.05), 10, 12), 0)))
Lights$Lights.Y <- c(round(runif(round(Desired.Lights * 0.35), 4, 6), 0),
round(runif(round(Desired.Lights * 0.20), 7, 8), 0),
round(runif(round(Desired.Lights * 0.17), 9, 10), 0),
round(runif(round(Desired.Lights * 0.13), 11, 12), 0),
round(runif(round(Desired.Lights * 0.10), 13, 14), 0),
round(runif(round(Desired.Lights * 0.05), 15, 17), 0))
Lights$Lights.Colour <- c(round(runif(Total.Lights, 1, 4), 0))
# Generate the "baubles"
Baubles <- data.frame(Bauble.X = c(6, 9, 15, 17, 5, 13, 16, 7, 10, 14, 7, 9, 11,
14, 8, 14, 9, 12, 11, 12, 14, 11, 17, 10))
Baubles$Bauble.Y <- c(4, 5, 4, 4, 5, 5, 5, 6, 6, 6, 8, 8, 8, 8, 10,
10, 11, 11, 12, 13, 10, 16, 7, 14)
Baubles$Bauble.Colour <- factor(c(1, 2, 2, 3, 2, 3, 1, 3, 1, 1, 1, 2, 1, 2,
3, 3, 2, 1, 3, 2, 1, 3, 3, 1))
Baubles$Bauble.Size <- c(1, 3, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 3, 3, 3,
2, 3, 1, 1, 2, 2, 3, 3, 2)
# Generate the plot
ggplot() +
geom_tile(data = ChristmasTree, aes(x = Tree.X, y = Tree.Y, fill = Tree.Colour)) +
scale_fill_identity() +
geom_point(data = Lights, aes(x = Lights.X, y = Lights.Y, alpha = Lights.Colour),
colour = "lightgoldenrodyellow", shape = 16) +
geom_point(data = Baubles, aes(x = Bauble.X, y = Bauble.Y, colour = Bauble.Colour, size = Bauble.Size),
shape = 16) +
scale_colour_manual(values = c("firebrick2", "gold", "dodgerblue3")) +
scale_size_area(max_size = 12) +
theme_bw() +
scale_x_continuous(breaks = NULL) +
scale_y_continuous(breaks = NULL) +
geom_segment(aes(x = 2.5, xend = 4.5, y = 1.5, yend = 1.5), colour = "blueviolet", size = 2) +
geom_segment(aes(x = 5.5, xend = 8.5, y = 1.5, yend = 1.5), colour = "dodgerblue3", size = 2) +
geom_segment(aes(x = 13.5, xend = 16.5, y = 1.5, yend = 1.5), colour = "blueviolet", size = 2) +
geom_segment(aes(x = 17.5, xend = 19.5, y = 1.5, yend = 1.5), colour = "dodgerblue3", size = 2) +
geom_segment(aes(x = 3.5, xend = 3.5, y = 0.5, yend = 2.5), colour = "blueviolet", size = 2) +
geom_segment(aes(x = 7.0, xend = 7.0, y = 0.5, yend = 2.5), colour = "dodgerblue3", size = 2) +
geom_segment(aes(x = 15.0, xend = 15.0, y = 0.5, yend = 2.5), colour = "blueviolet", size = 2) +
geom_segment(aes(x = 18.5, xend = 18.5, y = 0.5, yend = 2.5), colour = "dodgerblue3", size = 2) +
annotate("text", x = 11, y = 20, label = "Merry Christmas!",
size = 12) +
labs(x = "", y = "") +
theme(legend.position = "none")

LS0tCnRpdGxlOiAiVGhlIGdncGxvdCBhZHZlbnQgY2FsZW5kYVIhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpIZXJlIGl0IGlzOiB0aGUgZ2dwbG90IGFkdmVudCBjYWxlbmRhUiEgVGhlICJnZyIgaW4gZ2dwbG90IHJlZmVycyB0byB0aGUgZ3JhbW1hciBvZiBncmFwaGljcy4gRm9yIHRoZSBuZXh0IDI1IGRheXMsIHdlIHdpbGwgZ28gdGhyb3VnaCBhbiBpbnRyb2R1Y3Rpb24gdG8gdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MsIG1ha2UgYSBsb3Qgb2YgdmlzdWFsaXphdGlvbnMgKHNvbWUgZ29vZCwgc29tZSBiYWQpLCBhbmQgbGVhcm4gc29tZSBvZiB0aGUgYmFzaWMgZnVuY3Rpb25zIGFuZCBmZWF0dXJlcyBvZiB0aGUgZ2dwbG90MiBwYWNrYWdlLiAqKk5PVEUqKiBJIGFtIG5vIGV4cGVydCBpbiBnZ3Bsb3QuIEkgbGl0ZXJhbGx5IGxlYXJuZWQgd2hpbGUgY3JlYXRpbmcgdGhpcyB0dXRvcmlhbCwgYW5kIHRoYXQgd2FzIGEgYmlnIG1vdGl2YXRpb24gaW4gZG9pbmcgdGhpcy4gSWYgdGhlcmUgYXJlIGVycm9ycyBvciBzbW9vdGhlciB3YXlzIHRvIGRvIHRoZSBzYW1lIHRoaW5nLCBwbGVhc2UgbGV0IG1lIGtub3chIFIgaXMgYWJvdXQgY29uc3RhbnQgbGVhcm5pbmcgYW5kIGltcHJvdmluZywgZ2dwbG90IGlzIG5vIGRpZmZlcmVudC4KCkRBWSAxCgpPbiB0aGUgZmlyc3QgZGF5IG9mIENocmlzdG1hcy4uLiB3ZSdyZSBqdW1waW5nIGludG8gdGhlIHRpZHl2ZXJzZSEKCmdncGxvdCBpcyBwYXJ0IG9mIHRoZSB0aWR5dmVyc2UsIGEgZ3JvdXAgb2YgcGFja2FnZXMgdGhhdCBhbHNvIGluY2x1ZGVzIGRwbHlyLCByZWFkciwgYW5kIG90aGVyIHZlcnkgaGVscGZ1bCBwYWNrYWdlcyB0aGF0IHlvdSBzaG91bGQgaGF2ZSEgWW91IGNhbiBpbnN0YWxsIGFuZCBsb2FkIGdncGxvdCBzZXBhcmF0ZWx5LCBidXTigKYgd2h5PyAoOgoKVGhlIHBhY2thZ2Ugd2UncmUgdXNpbmcgaXMgYWN0dWFsbHkgY2FsbGVkIGdncGxvdDIuIFN1cGVyLWR1cGVyIGZ1biBmYWN0OiAiZ2dwbG90MiBpcyBjYWxsZWQgZ2dwbG90MiBiZWNhdXNlIG9uY2UgdXBvbiBhIHRpbWUgdGhlcmUgd2FzIGp1c3QgYSBsaWJyYXJ5IGdncGxvdC4gSG93ZXZlciwgdGhlIGRldmVsb3BlciBub3RpY2VkIHRoYXQgaXQgdXNlZCBhbiBpbmVmZmljaWVudCBzZXQgb2YgZnVuY3Rpb25zLiBJbiBvcmRlciBmb3Igbm90IHRvIGJyZWFrIHRoZSBBUEksIHRoZSBhdXRob3JzIGludHJvZHVjZWQgYSBzdWNjZXNzb3IgcGFja2FnZSBnZ3Bsb3QyLiBIb3dldmVyLCB0aGUgY2VudHJhbCBmdW5jdGlvbiBpbiB0aGlzIHBhY2thZ2UgaXMgc3RpbGwgY2FsbGVkIGdncGxvdCgpLCBub3QgZ2dwbG90MigpISIgKHdhc24ndCB0aGF0IGZ1bj8gU291cmNlOiBGcmVlbWFuICYgUm9zcywgMjAxOSkuCgpJbnN0YWxsIGFuZCBsb2FkIHRpZHl2ZXJzZToKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCkxldCdzIGFsc28gbG9hZCBvdXIgZGF0YSB1c2luZyBhbm90aGVyIHRpZHl2ZXJzZSBwYWNrYWdlLCByZWFkciwgYW5kIHRoZW4gdmlldyB0aGUgZGF0YS4gV2Ugd2lsbCBiZSB3b3JraW5nIHdpdGggdHdvIGRhdGFzZXRzIHRocm91Z2hvdXQgdGhpcyB0dXRvcmlhbDoKYGBge3J9CnNsZWlnaHMgPC1yZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2tpaXJzdGkvZ2dwbG90X2FkdmVudGNhbGVuZGFSL21haW4vc2xlaWdoLmRhdGEuY3N2IikKc2xlaWdocyAjaWYgeW91IGhpZ2hsaWdodCBvciByZXR5cGUgYW5kIHJ1biB0aGUgbmFtZSAic2xlaWdocyIgaXQgd2lsbCBzaG93IHlvdSBhIHNhbXBsZSBvZiB0aGUgZGF0YQoKdHJlZXMgPC1yZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2tpaXJzdGkvZ2dwbG90X2FkdmVudGNhbGVuZGFSL21haW4veG1hcy50cmVlcy5jc3YiKQp0cmVlcwpgYGAKVXNpbmcgcmVhZHIgYW5kIHJlYWRfY3N2KCksIGluc3RlYWQgb2YgcmVhZC5jc3YoKSBmcm9tIGJhc2UgUiwgbG9hZHMgb3VyIGRhdGEgaW4gYXMgdGliYmxlcy4gV2h5IHVzZSB0aWJibGVzIG92ZXIgdHJhZGl0aW9uYWwgZGF0YWZyYW1lcz8gVGhyZWUgcmVhc29uczogKDEpIHRoZSBpbnB1dCB0eXBlcyBhcmVuJ3QgYXV0b21hdGljYWxseSBjaGFuZ2VkIHdoZW4geW91IHJlYWQgaW4gdGhlIGRhdGEsICgyKSB5b3UgY2FuIGtlZXAgbGlzdHMgYXMgY29sdW1ucywgYW5kICgzKSB5b3UgY2FuIHVzZSBub24tc3RhbmRhcmQgdmFyaWFibGUgbmFtZXMgKGUuZy4sIHN0YXJ0aW5nIHdpdGggYSBudW1iZXIsIGFzIGluICIxc3RfcGxhY2UiKS4gVGhhbmsgeW91IHRvIG15IGZyaWVuZCBQYXQgZm9yIHRoaXMgZXhwbGFuYXRpb24hCgpEQVkgMgoKT24gdGhlIHNlY29uZCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi53ZSBsZWFybmVkIHRoZSBsYW5ndWFnZSBvZiBnZ3Bsb3QgYWthIHRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzISBnZ3Bsb3QgaXMgZGlmZmVyZW50IGZyb20gYmFzZSBSIGdyYXBoaWNzLiBIb3c/IEJhc2UgUiBncmFwaGljcyB3b3JrIG9uIHRoZSBpbmRpdmlkdWFsIHZlY3RvcnMsIGdncGxvdCB3b3JrcyBvbiBkYXRhZnJhbWVzIChTb3VyY2U6IFByYWJoYWthcmFuLCAyMDE3KS4gZ2dwbG90IHdvcmtzIGJ5IGFkZGluZyBsYXllciB1cG9uIGxheWVyIHRvIGNyZWF0ZSB5b3VyIHZpc3VhbGl6YXRpb24uIEluIGJhc2UgUiBncmFwaGljcywgeW91IHB1dCBhbGwgdGhlIGluZm9ybWF0aW9uIGluIG9uZSBjb2RlIGFuZCBpdCBzcGl0cyBvdXQgYSBncmFwaGljLiBJbiBnZ3Bsb3QsIHlvdSBidWlsZCB5b3VyIGdyYWhwaWMgd2l0aCBsYXllcnMuCgpXaGF0IGFyZSBzb21lIG9mIHRoZSBkaWZmZXJlbnQgY29tcG9uZW50cyBvZiBhIGdyYXBoaWMgKFNvdXJjZTogRnJlZW1hbiAmIFJvc3MsIDIwMTkpPwotIGRhdGEKLSBnZW9tZXRyaWMgb2JqZWN0cyAoZ2VvbXMpCi0gYWVzdGhldGljcwotIHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9ucwotIHBvc2l0aW9uIGFkanVzdG1lbnRzCi0gc2NhbGUKLSBjb29yZGluYXRlIHN5c3RlbQotIGZhY2V0cwotIHRoZW1lcwoKVGhlIGZpcnN0IGxheWVyIG9mIGdncGxvdCBpcyBhbHdheXMuLi5nZ3Bsb3QuIElmIHlvdSBydW4gdGhlIGNvZGUgYmVsb3csIHlvdSB3aWxsIGdldCBhIGJsYW5rIGdyYXBoaWMgd2l0aCBhIGdyZXkgYmFja2dyb3VuZC4gVGhlIGdyZXkgYmFja2dyb3VuZCBpcyB0aGUgZ2dwbG90IGRlZmF1bHQuIEluIGJhc2UgUiwgeW91IGNhbid0IHJ1biBqdXN0IGZ1bmN0aW9uLCBlLmcuLCBib3hwbG90KCksIHdpdGhvdXQgYSB2ZWN0b3IgaW4gdGhlIGJyYWNrZXRzLgoKYGBge3J9CmdncGxvdCgpCmBgYApOb3cgbGV0J3MgYWRkIGFub3RoZXIgbGF5ZXIgdG8gZ2dwbG90LiBXZSdsbCBzcGVjaWZ5IHRoZSBkYXRhc2V0IGFuZCB3aGF0IHdlIHdhbnQgb3VyIHggYW5kIHkgYXhlcyB0byBiZS4KYGBge3J9CmdncGxvdCh0cmVlcywgYWVzKHg9dHlwZSwgeT1oZWlnaHQpKQpgYGAKV2hlbiB5b3UgcnVuIHRoaXMgY29kZSwgeW91IHNob3VsZCBzZWUgdGhhdCB3ZSBub3cgaGF2ZSBheGVzIGFuZCBsYWJlbHMgb24gdG9wIG9mIG91ciBncmV5IGJhY2tncm91bmQuIE5vdGUsIHlvdSBjYW4gZHJvcCB0aGUgeD0gYW5kIHk9IGFuZCB0aGUgY29kZSB3aWxsIHJ1biB0aGUgc2FtZSAoZG9uJ3QgdGFrZSBteSB3b3JkIGZvciBpdCwgdHJ5IGl0IHlvdXJzZWxmISkuCgoKREFZIDMKCk9uIHRoZSB0aGlyZCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi53ZSBhZGRlZCBkYXRhIHRvIG91ciBncmFwaGljIGFuZCBleHBsb3JlZCBnZW9tIGxheWVycyEKCkdlb21zLCBvciBnZW9tZXRyaWMgb2JqZWN0cywgYXJlIGdyYXBoaWNhbCByZXByZXNlbnRhdGlvbnMgb2YgdGhlIGRhdGEuIFRoZXJlIGFyZSBtYW55IG1hbnkgdHlwZXMgb2YgZ2VvbXMgKGhlcmUncyBhIGxvbmcgbGlzdCBvZiBleGFtcGxlczogaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlLyNnZW9tcykuIExldCdzIHRyeSBhIGZldy4KCkdlb20gbGF5ZXJzIHN0YXJ0IHdpdGggImdlb21fIiBhbmQgYXJlIGZvbGxvd2VkIGJ5IHRoZSB0eXBlIG9mIGdlb21ldHJpYyBvYmplY3QsIGUuZy4sICJnZW9tX3BvaW50IiBvciAiZ2VvbV9saW5lIi4gQmVjYXVzZSB3ZSdyZSBhZGRpbmcgYSBuZXcgbGF5ZXIgb250byBvdXIgZ3JhcGhpYywgd2UgdXNlIGEgKyBhZnRlciBvdXIgZmlyc3QgbGluZSBvZiBjb2RlLiBUbyBrZWVwIHRoaW5ncyB0aWR5IGFuZCBlYXN5IHRvIHJlYWQsIEkgdXN1YWxseSBzdGFydCBteSBuZXcgbGF5ZXIgb24gYSBuZXcgbGluZS4KCmBgYHtyfQpnZ3Bsb3QodHJlZXMsIGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9wb2ludCgpCmBgYApTaW5jZSB3ZSBhcmUgd29ya2luZyB3aXRoIGNhdGVnb3JpY2FsIGRhdGEgKHR5cGVzIG9mIHRyZWVzKSwgdGhlIHBvaW50cyBhbGwgZmFsbCBvbiB0aHJlZSBsaW5lcy4gTGV0J3MgY2hhbmdlIG91ciB4IGFuZCB5IGF4ZXMgYmVmb3JlIG1vdmluZyBvbiB0byBhIGRpZmZlcmVudCB0eXBlIG9mIGdlb20uCgpgYGB7cn0KZ2dwbG90KHRyZWVzLCBhZXMoeD14bWFzLm1hZ2ljLCB5PWhlaWdodCkpKwogIGdlb21fcG9pbnQoKQpgYGAKTm93IHdlJ3JlIGNvbXBhcmluZyB0d28gY29udGludW91cyB2YXJpYWJsZXMgKHhtYXMgbWFnaWMgYW5kIHRyZWUgaGVpZ2h0KSBzbyB0aGUgcG9pbnRzIGFyZSBzY2F0dGVyZWQgYWNyb3NzIG91ciBncmFwaGljLgoKTGV0J3MgbG9vayBhdCBzb21lIGRpZmZlcmVudCBnZW9tIHR5cGVzLiBXZSdsbCBnbyBiYWNrIHRvIHR5cGUgdnMuIGhlaWdodCBmb3IgdGhpcyBvbmUuCgpgYGB7cn0KZ2dwbG90KHRyZWVzLCBhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fYm94cGxvdCgpCmBgYApXaGVuIGNyZWF0aW5nIGdyYXBoaWNzLCBhbHdheXMgY29uc2lkZXIgdGhlIHR5cGUgb2YgZGF0YSB5b3UncmUgd29ya2luZyB3aXRoIChlLmcuLCBjb250aW51b3VzIHZzLiBkaXNjcmV0ZSkuIFRoZSB0eXBlIG9mIGRhdGEgeW91J3JlIHdvcmtpbmcgd2l0aCBzaG91bGQgZGV0ZXJtaW5lIHRoZSB0eXBlIG9mIGdlb20geW91IGNob29zZS4gU29tZSBnZW9tcyB3b24ndCBydW4gcHJvcGVybHkgaWYgdGhlIHR5cGUgb2YgZGF0YSB5b3UncmUgaW5wdXR0aW5nIGRvZXNuJ3Qgd29yayB3aXRoIHRob3NlIGRhdGEgdHlwZXMuCgpPbmUgbW9yZSBleGFtcGxlIG9mIGEgZ2VvbSBsYXllciB1c2luZyB0aGUgdHJlZXMgZGF0YS4KCmBgYHtyfQpnZ3Bsb3QodHJlZXMsIGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV92aW9saW4oKQpgYGAKCkRBWSA0CgpPbiB0aGUgZm91cnRoIGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLndlIGludHJvZHVjZWQgcGlwZXMhCgpUaGlzIGlzIGEgYml0IG9mIGEgc2VndWUgZnJvbSBnZ3Bsb3QgYW5kIHlvdSBjZXJ0YWlubHkgZG9uJ3QgbmVlZCB0byB1c2UgcGlwZXMgdG8gY3JlYXRlIGJlYXV0aWZ1bCBnZ3Bsb3QgZ3JhcGhpY3MgQlVUIGl0IG1pZ2h0IG1ha2UgeW91ciBkYXRhIGxvb2sgbW9yZSB0aWR5LgoKVGhlIHBpcGUgb3BlcmF0b3IgKCU+JSkgaXMgaW5jbHVkZWQgaW4gdGhlIHRpZHl2ZXJzZSwgc28gaWYgeW91IGxvYWRlZCB0aGUgdGlkeXZlcnNlLCB0aGVuIHlvdSd2ZSBnb3QgYWNjZXNzIHRvICU+JS4gRnJvbSBSIGZvciBEYXRhIFNjaWVuY2UsICJQaXBlcyBhcmUgYSBwb3dlcmZ1bCB0b29sIGZvciBjbGVhcmx5IGV4cHJlc3NpbmcgYSBzZXF1ZW5jZSBvZiBtdWx0aXBsZSBvcGVyYXRpb25zLi4uVGhlIHBvaW50IG9mIHRoZSBwaXBlIGlzIHRvIGhlbHAgeW91IHdyaXRlIGNvZGUgaW4gYSB3YXkgdGhhdCBpcyBlYXNpZXIgdG8gcmVhZCBhbmQgdW5kZXJzdGFuZC4iIEluIHdvcmRzLCAlPiUgbWVhbnMgImFuZCB0aGVuIi4gQnkgdXNpbmcgYSBwaXBlLCB5b3UncmUgdGVsbGluZyBSIHRvIHJ1biB0aGUgZmlyc3QgbGluZSBvZiBjb2RlIEFORCBUSEVOIHJ1biB0aGUgbmV4dCBsaW5lIG9mIGNvZGUuCgpXaHkgZG8gSSBicmluZyB0aGlzIHVwIG5vdz8gUHJldmlvdXNseSwgd2UgcmFuIHRoaXMgY29kZSB0byBwcm9kdWNlIGEgYm94cGxvdCBzaG93aW5nIHRyZWUgaGVpZ2h0czoKCmBgYHtyfQpnZ3Bsb3QodHJlZXMsIGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpPZnRlbiwgaW4gb25saW5lIGV4YW1wbGVzIGFuZCB0dXRvcmlhbHMsIHlvdSB3aWxsIHNlZSBnZ3Bsb3QgY29kZXMgd3JpdHRlbiB3aXRoIHRoZSBkYXRhZnJhbWUgbGlzdGVkIGZpcnN0LCB0aGVuIGEgcGlwZSBsZWFkaW5nIGludG8gdGhlIGdncGxvdCBjb2RlLiBJZiB5b3UgcnVuIHRoZSBjb2RlIGJlbG93LCB5b3UnbGwgc2VlIHRoYXQgd2UgY2FuIHByb2R1Y2UgdGhlIGV4YWN0IHNhbWUgZ3JhcGhpYzoKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX2JveHBsb3QoKQpgYGAKQW5vdGhlciB0aXAgZnJvbSBteSBmcmllbmQgUGF0LCAiVGhlIG1vc3QgdXNlZnVsIGFwcGxpY2F0aW9uIG9mIHRoZSBwaXBlIGlzIHRvIHBsdWcgdGhlIHJlc3VsdCBvZiBvbmUgZnVuY3Rpb24gaW50byBhbm90aGVyIHdpdGhvdXQgY3JlYXRpbmcgaW50ZXJtZWRpYXRlIGRhdGEgZnJhbWVzIiBIZXJlJ3MgYW4gZXhhbXBsZSBvZiB3aGF0IHRoaXMgbWlnaHQgbG9vayBsaWtlOgoKbXlfZGF0YSAlPiUKICBmdW5jdGlvbjEoYXJndW1lbnRzLCBldGMpICU+JQogIGZ1bmN0aW9uMihhcmd1bWVudHMsIGV0YykKCgpEQVkgNQoKT24gdGhlIGZpZnRoIGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLndlIHN0YXJ0ZWQgd29ya2luZyB3aXRoIHRoZSBhZXN0aGV0aWNzIG9mIG91ciBncmFwaGljIQoKQWVzdGhldGljIG1hcHBpbmcgZGVzY3JpYmVzIHRoZSB2aXN1YWwgcHJvcGVydGllcyBvZiB0aGUgZ3JhcGhpYy4gV2UgY2FuIGNoYW5nZSB0aGUgYWVzdGhldGljcyBvZiBnZ3Bsb3QoKSBvciBlYWNoIGxheWVyIHdlIGFkZCB0byBpdC4gV2UgdXNlIGFlcygpIGFuZCB0aGVuIGN1c3RvbWl6ZSBvdXIgZ3JhcGhpYyB0byBhcHBlYXIgaG93IHdlJ2QgbGlrZSBpdC4gT24gRGF5IDMsIHdlIGNyZWF0ZWQgc29tZSBwcmV0dHkgYm9yaW5nIGdyYXBoczogd2hpdGUgYm94ZXMgb3IgdmlvbGlucywgYmxhY2sgZG90cywgYW5kIGdyZXkgYmFja2dyb3VuZHMuIFdoaWxlIHRoaXMgaXMgZmluZSBmb3IgZXhwbG9yaW5nIGRhdGEsIGl0J3MgcGVyaGFwcyBub3QgaG93IHdlIHdhbnQgb3VyIGZpbmlzaGVkIHByb2R1Y3QgdG8gbG9vayEKClRvZGF5LCB3ZSdyZSBnb2luZyB0byB3b3JrIG9uIGNoYW5naW5nIHRoZSBjb2xvdXJzIG9mIG91ciBkYXRhLiBMZXQncyBzdGFydCB3aXRoIHRoZSBzY2F0dGVycGxvdCwgaS5lLiwgdGhlIGdlb21fcG9pbnQoKSBncmFwaGljLgoKVG8gY2hhbmdlIHRoZSBhZXN0aGV0aWNzIG9mIHRoZSBnZW9tX3BvaW50KCkgZG90cywgd2UgYWRkICJhZXMoKSIgd2l0aGluIHRoZSBwYXJlbnRoZXNlcyBvZiBnZW9tX3BvaW50KCkuIFlvdSBtYXkgaGF2ZSBub3RpY2VkIHRoYXQgd2UgYWxyZWFkeSB1c2VkICJhZXMoKSIgd2hlbiB3ZSB0b2xkIGdncGxvdCB3aGF0IHdlIHdhbnRlZCBvdXIgWCBhbmQgWSBheGVzIHRvIGJlLiBOb3cgd2Ugd2lsbCBhbHNvIHRlbGwgZ2dwbG90IHRvIGFzc2lnbiBkaWZmZXJlbnQgY29sb3VycyB0byBvdXIgcG9pbnRzIGJ5IHRyZWUgdHlwZS4gSW4gdGhpcyBjYXNlLCB3ZSBhcmUgbm90IGNob29zaW5nIHRoZSBjb2xvdXJzLgoKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQsIGNvbG91cj10eXBlKSkrCiAgZ2VvbV9wb2ludCgpCmBgYApOb3cgaW5zdGVhZCBvZiBjaGFuZ2luZyB0aGUgYWVzdGhldGljcyBvZiB0aGUgZ2dwbG90KCkgbGF5ZXIsIGxldCdzIGluc3RlYWQgY2hhbmdlIHRoZSBhZXN0aGV0aWNzIG9mIHRoZSBnZW9tX3BvaW50KCkgbGF5ZXIuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KCkrCiAgZ2VvbV9wb2ludChhZXMoeD10eXBlLCB5PWhlaWdodCwgY29sb3VyPXR5cGUpKQpgYGAKTG9va3MgdGhlIHNhbWUgcmlnaHQ/IFNvIHdoYXQncyB0aGUgZGlmZmVyZW5jZT8gUmlnaHQgbm93LCBub3RoaW5nIGFwcGVhcnMgZGlmZmVyZW50LCBidXQgbGF0ZXIgaXQgbWF5IGltcGFjdCBob3cgeW91ciBncmFwaGljIGxvb2tzLiBXaGVuIHlvdSBjaGFuZ2UgdGhlIGFlc3RoZXRpY3Mgb2YgdGhlIGdncGxvdCgpIGxheWVyLCB0aG9zZSBhZXN0aGV0aWNzIHdpbGwgYmUgYXBwbGllZCBhcyB0aGUgZGVmYXVsdCB0byBhbGwgb2YgeW91ciBzdWJzZXF1ZW50IGxheWVycy4gSWYgeW91IGNoYW5nZSB0aGUgYWVzdGhldGljcyBvZiBhbiBpbmRpdmlkdWFsIGxheWVyLCBpdCB3aWxsIG9ubHkgYmUgYXBwbGllZCB0byB0aGF0IGxheWVyIGFuZCBpdCB3aWxsIG92ZXJyaWRlIHRoZSBkZWZhdWx0LgoKV2hhdCBpZiB3ZSB3YW50IHRvIHVzZSBhbGwgdGhlIHNhbWUgY29sb3VyIGZvciBvdXIgcG9pbnRzPyBGaW5kIG91dCB0b21vcnJvdyEKCkRBWSA2CgpPbiB0aGUgc2l4dGggZGF5IG9mIENocmlzdG1hcy4uLgoKLi4ud2UgY29udGludWVkIHdpdGggYWVzdGhldGljIG1hcHBpbmcgYW5kIGNvbG91cnMhCgpTbyB5b3Ugd2FudCBhbGwgeW91ciBwb2ludHMgdG8gYmUgb25lIGNvbG91ci4gU291bmRzIGVhc3ktcGVhc3kgcmlnaHQ/IE5vdCBxdWl0ZS4KCkhlcmUncyB3aHkgaXQncyBhIGJpdCBjb25mdXNpbmcuIEkgaGFkIHRvIGRvIGEgYml0IG9mIGRpZ2dpbmcgdG8gdW5kZXJzdGFuZCBleGFjdGx5IHdoeSB0aGlzIGlzLi4uCgpXaGVuIHdlIHdhbnQgdG8gbWFwIGEgdmFyaWFibGUgb2Ygb3VyIGRhdGEgKGUuZy4sIHRlbGxpbmcgZ2dwbG90IHdlIHdhbnQgdG8gY29sb3VyIGJ5IHRyZWUgdHlwZSksIHdlIHB1dCBhZXMoKSBpbnNpZGUgdGhlIGdlb21fcG9pbnQoKS4gSWYgd2Ugd2FudCB0byBhcHBseSBhIGNvbnN0YW50IGNvbG91ciAoY29uc3RhbnQgdmFsdWUpIHRvIG91ciBwb2ludHMgKGUuZy4sIHRlbGxpbmcgZ2dwbG90IHdlIHdhbnQgYWxsIG91ciBwb2ludHMgdG8gYmUgYmx1ZSksIHdlIHB1dCBhZXMoKSBPVVRTSURFIGdlb21fcG9pbnQpLiBUcnkgYm90aCB0aGUgY29kZXMgYmVsb3cgdG8gc2VlIHdoYXQgaGFwcGVucy4gCgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fcG9pbnQoYWVzKGNvbG91cj0iYmx1ZSIpKQoKdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fcG9pbnQoYWVzKCksIGNvbG91cj0iYmx1ZSIpCmBgYApOb3RlOiBpbiB0aGlzIGNhc2UgeW91IGNvdWxkIGFsc28gbGVhdmUgb3V0IHRoZSBhZXMoKSB3aXRoaW4gZ2VvbV9wb2ludCgpIGFuZCBpdCB3b3VsZCBydW4gdGhlIHNhbWUuIAoKRm9yIG1vcmUgb2YgYW4gZXhwbGFuYXRpb24gb24gd2h5IGFlcygpIHdvcmtzIHRoaXMgd2F5LCB5b3UgY2FuIGNoZWNrIG91dCB0aGlzIGhlbHBmdWwgdGhyZWFkIG9uIHN0YWNrb3ZlcmZsb3cgdGhhdCBoZWxwZWQgbWU6IGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQxODYzMDQ5L3doZW4tZG9lcy10aGUtYXJndW1lbnQtZ28taW5zaWRlLW9yLW91dHNpZGUtYWVzLiBIZXJlJ3MgYW5vdGhlciByZWFsbHkgZmFudGFzdGljIHJlc291cmNlOiBodHRwczovL2RyaXZlLmdvb2dsZS5jb20vZmlsZS9kLzFEdnVsMXA2VFlINmdXSnpaUndwRTBZWDFkTzBoREYtYi92aWV3LgoKREFZIDcKCk9uIHRoZSBzZXZlbnRoIGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLndlIGNoYW5nZWQgdGhlIGNvbG91cnMgb2YgYm94cGxvdHMhCgpUcnkgcnVubmluZyB0aGUgc2FtZSBjb2RlIGZyb20geWVzdGVyZGF5IGJ1dCBvbiBhIGdlb21fYm94cGxvdCgpIGdyYXBoaWMuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KGFlcygpLCBjb2xvdXI9ImJsdWUiKQpgYGAKSG1tLi4uIG9rYXkuIEJ1dCB3aGF0IGlmIHdlIHdhbnRlZCB0aGUgaW5zaWRlIG9mIHRoZSBib3hlcyB0byBiZSBjb2xvdXJlZCwgbm90IHRoZSBvdXRsaW5lPwoKSW5zdGVhZCBvZiAiY29sb3VyID0iIHdlIHVzZSAiZmlsbCA9IiBpbnN0ZWFkLgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fYm94cGxvdChhZXMoKSwgZmlsbD0iYmx1ZSIpCmBgYAoKVGhpcyB3b3JrcyBmb3IgcG9pbnRzIHRvby4gVGhlIGRlZmF1bHQgZm9yIGdlb21fcG9pbnQoKSBhcmUgc29saWQgcG9pbnRzLCBidXQgeW91IGNhbiBjaGFuZ2UgdGhlc2UgdG8gcG9pbnRzIHdpdGggZGlmZmVyZW50IG91dGxpbmUgYW5kIGZpbGwgY29sb3Vycy4gCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9wb2ludChhZXMoKSwgc2hhcGUgPSAyMSwgZmlsbD0iZ3JlZW40IiwgY29sb3VyPSJyZWQiKQpgYGAKV2UgY2FuIGFsc28gY2hhbmdlIHRoZSBjb2xvdXJzIG9mIG91ciBib3hwbG90cyBieSB0cmVlIHR5cGUsIGFzIHdlIGRpZCB3aXRoIHRoZSBwb2ludHMgb24gRGF5IDUuIFJlbWVtYmVyIGZyb20geWVzdGVyZGF5IHRoYXQgYmVjYXVzZSB3ZSB3YW50IHRvIGNoYW5nZSB0aGUgY29sb3VycyBieSBhIHZhcmlhYmxlIChpLmUuLCBtdWx0aXBsZSBjb2xvdXJzIGRldGVybWluZCBieSB0aGUgbGV2ZWxzIG9mIHRoZSB2YXJpYWJsZSkgcmF0aGVyIHRoYW4gY2hhbmdlIHVzaW5nIGEgY29uc3RhbnQgdmFsdWUgKGkuZS4sIHNpbmdsZSBjb2xvdXIpLCB3ZSBwdXQgdGhpcyBJTlNJREUgYWVzKCkuCgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fYm94cGxvdChhZXMoZmlsbD10eXBlKSwgY29sb3VyPSJibGFjayIpCmBgYAoKTm93IHRoYXQgd2UncmUgcGxheWluZyBhcm91bmQgd2l0aCBjb2xvdXJzLiBJdCdzIHRpbWUgSSBpbnRyb2R1Y2UgeW91IHRvIGEgZ29vZCBjb2xvdXIgcmVzb3VyY2U6IGh0dHA6Ly93d3cuc3RhdC5jb2x1bWJpYS5lZHUvfnR6aGVuZy9maWxlcy9SY29sb3IucGRmLiBUaGVyZSBhcmUgbWFueSBncmVhdCBvbmxpbmUgcmVzb3VyY2VzIGFib3V0IGNvbG91cnMsIGJ1dCBJIGxpa2UgdGhpcyBvbmUgbWFkZSBieSBEci4gWWluZyBXZWkuCgpEQVkgOAoKT24gdGhlIGVpZ2h0IGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLndlIGZpbmFsbHkgdG9vayBhIGJyZWFrIGZyb20gdGFsa2luZyBhYm91dCBjb2xvdXJzIQoKQ29sb3VyIGlzIGEgYmlnIG9uZSwgYnV0IGxldCdzIHdvcmsgd2l0aCBzb21lIG90aGVyIGNoYW5nZXMgdG8gdGhlIGFlc3RoZXRpY3Mgb2Ygb3VyIGdyYXBoaWMuIExldCdzIGJyaW5nIGJhY2sgdGhlIGdlb21fcG9pbnQoKSBncmFwaGljLiBJJ20gdXNpbmcgdGhlIHRoZW1lIG9mIENocmlzdG1hcywgYnV0IG9mIGNvdXJzZSwgRGVjZW1iZXIgaGFzIG1hbnksIG1hbnkgaG9saWRheXMuIEhhbnVra2FoIHN0YXJ0cyBvbiBEZWMuIDE3IHRoaXMgeWVhciwgc28gbGV0J3MgbWFrZSBhIEhhbnVra2FoLWluc3BpcmVkIGdyYXBoaWMgYW5kIGxvb2sgYXQgZGlmZmVyZW50IHdheXMgdG8gY2hhbmdlIHRoZSBhZXN0aGV0aWNzIG9mIG91ciBncmFwaHMuCgoKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX3BvaW50KGFlcygpLCBzaGFwZSA9IDExLCBjb2xvdXI9ImJsdWUiLCBzaXplID0gNiwgc3Ryb2tlPTIpCmBgYAoKSGVyZSB3ZSd2ZSBzcGVjaWZpZWQgdGhlIHNoYXBlLCB0aGUgY29sb3VyLCB0aGUgc2l6ZSwgYW5kIHRoZSBzdHJva2UgKGxpbmUgdGhpY2tuZXNzKSBvZiB0aGUgcG9pbnRzLiBUaGVyZSBhcmUgbWFueSBjaGFuZ2VzIHdlIGNhbiBtYWtlIHRvIGFlc3RoZXRpY3MsIHRoZXNlIGFyZSBqdXN0IGEgZmV3IGV4YW1wbGVzLiBMZXQncyB0YWtlIGEgbG9vayBhdCBhIGxpbmUgZ3JhcGggYW5kIGhvdyB3ZSBjYW4gY3VzdG9taXplIHRoYXQuCgpXZSBoYXZlbid0IG1hZGUgYSBsaW5lIGdyYXBoIHlldCwgc28gbGV0J3MgY3JlYXRlIGEgc2ltcGxlIG9uZSBmaXJzdCwgYmVmb3JlIHdlIGN1c3RvbWl6ZSBpdC4KCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiAgZ2VvbV9saW5lKCkKYGBgCkhlcmUgd2UndmUgc3BlY2lmaWVkIHRoYXQgd2Ugd2FudCBlYWNoIGxpbmUgdG8gcmVwcmVzZW50IGEgZGlmZmVyZW50IHRyZWUgdHlwZSBieSB1c2luZyAiZ3JvdXA9Ii4gVGhpcyBnb2VzIGluc2lkZSBhZXMoKSBiZWNhdXNlIGl0IGlzIGJlaW5nIGFwcGxpZWQgdG8gdGhlIGRhdGEgKGkuZS4sIGl0IGNhbid0IGJlIGRvbmUgd2l0aG91dCB0aGlzIHNwZWNpZmljIGRhdGFzZXQpCgpOb3cgbGV0J3Mgc3BydWNlIGl0IHVwLiAoInNwcnVjZSIgaXQgdXAuLi4gYmVjYXVzZSB0aGV5J3JlIHRyZWVzLi4uIGhhaGFoYWhhLiBBIGxpdHRsZSBDaHJpc3RtYXMgY2hlZXIgZm9yIHlvdSkuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiAgZ2VvbV9saW5lKGFlcyhjb2xvdXI9dHlwZSksIGxpbmV0eXBlPSJkYXNoZWQiKQpgYGAKV2hhdCBpZiB3ZSB3YW50IHRvIHNob3cgdGhlIHBvaW50cyBBTkQgdGhlIGxpbmVzPyBUb21vcnJvdyB3ZSB3aWxsIGFkZCBhbm90aGVyIGxheWVyIHRvIG91ciBncmFwaGljLgoKREFZIDkKCk9uIHRoZSBuaW50aCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi4gd2UgYWRkZWQgYW5vdGhlciBnZW9tXyBsYXllciB0byBvdXIgZ3JhcGhpYy4KCkhlcmUncyBvdXIgZ2VvbV9saW4oKSBncmFwaGljIGZyb20geWVzdGVyZGF5OgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD1oZWlnaHQsIHk9eG1hcy5tYWdpYywgZ3JvdXA9dHlwZSkpKwogIGdlb21fbGluZShhZXMoY29sb3VyPXR5cGUpLCBsaW5ldHlwZT0iZGFzaGVkIikKYGBgCgpOb3cgbGV0J3MgYWRkIHBvaW50cyB0byBpdCBhcyB3ZWxsLiBJdCdzIGFzIHNpbXBsZSBhcyArIGdlb21fcG9pbnQoKSEKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9aGVpZ2h0LCB5PXhtYXMubWFnaWMsIGdyb3VwPXR5cGUpKSsKICBnZW9tX2xpbmUoYWVzKGNvbG91cj10eXBlKSwgbGluZXR5cGU9ImRhc2hlZCIpKwogIGdlb21fcG9pbnQoKQpgYGAKCkFuZCBpZiB5b3Ugd2FudCB0byBjaGFuZ2UgdGhlIGFlc3RoZXRpY3Mgb2YgdGhvc2UgcG9pbnRzLCB3aGVyZSBkbyB5b3UgZG8gaXQ/IFdpdGhpbiBnZW9tX3BvaW50KCkhCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiAgZ2VvbV9saW5lKGFlcyhjb2xvdXI9dHlwZSksIGxpbmV0eXBlPSJkYXNoZWQiKSsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXI9dHlwZSksIHNpemUgPSAzLCBzaGFwZSA9IDgpCmBgYAoKVGhleSBraW5kIG9mIGxvb2sgbGlrZSBDaHJpc3RtYXMgbGlnaHRzIDopCgpEQVkgMTAKCk9uIHRoZSB0ZW50aCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi53ZSBlZGl0ZWQgdGhlIHRleHQgb2Ygb3VyIGdyYXBoaWNzLgoKRWRpdGluZyB0aGUgbG9vayBvZiB5b3VyIHRleHQgYW5kIGZvbnRzIGluIGdncGxvdCBpcyBlYXN5LCB3aXRoIGxvdHMgb2Ygb3B0aW9ucyB0byBtYWtlIGl0IGxvb2sgZXhhY3RseSBob3cgeW91IHdhbnQgaXQuIEhlcmUncyBvdXIgZ3JhcGhpYyBmcm9tIHllc3RlcmRheS4KYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9aGVpZ2h0LCB5PXhtYXMubWFnaWMsIGdyb3VwPXR5cGUpKSsKICBnZW9tX2xpbmUoYWVzKGNvbG91cj10eXBlKSwgbGluZXR5cGU9ImRhc2hlZCIpKwogIGdlb21fcG9pbnQoYWVzKGNvbG91cj10eXBlKSwgc2l6ZSA9IDMsIHNoYXBlID0gOCkKYGBgCldoYXQga2luZCBvZiB0aGluZ3MgbWlnaHQgd2Ugd2FudCB0byBjaGFuZ2U/IE1heWJlIHdlIHdhbnQgYSB0aXRsZSBhbmQgbW9yZSBpbmZvcm1hdGl2ZSBhbmQgY2xlYW5lci1sb29raW5nIGF4aXMgbGFiZWxzLiBUbyBkbyB0aGlzLCB3ZSBuZWVkIHRvIGFkZCBuZXcgbGF5ZXIsIGxhYnMoKSBmb3IgbGFiZWxzLiBZb3UgY2FuIGFsc28gdXNlIHhsYWIoKSwgeWxhYigpLCBhbmQgZ2d0aXRsZSgpIHRvIGFkZCB0aGVtIGluZGl2aWR1YWxseS4KCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiAgZ2VvbV9saW5lKGFlcyhjb2xvdXI9dHlwZSksIGxpbmV0eXBlPSJkYXNoZWQiKSsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXI9dHlwZSksIHNpemUgPSAzLCBzaGFwZSA9IDgpKwogICAgbGFicyh0aXRsZT0iQ2hyaXN0bWFzIFRyZWVzIiwgeD0iVHJlZSBoZWlnaHQiLCB5PSJDaHJpc3RtYXMgbWFnaWMiKQpgYGAKV2hhdCBpZiB3ZSB3YW50IHRvIG1ha2UgbW9yZSBhZXN0aGV0aWMgY2hhbmdlcyB0byBvdXIgZm9udHMgYW5kIGxhYmVscz8gV2Ugd2lsbCBuZWVkIHRvIHVzZSB0aGVtZXMhCgpEQVkgMTEKCk9uIHRoZSBlbGV2ZW50aCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi53ZSBpbnRyb2R1Y2VkIHRoZW1lcyEKCldlIGNhbiB1c2UgdGhlbWVzIHRvIG1ha2UgZmluZXIgYWRqdXN0bWVudHMgdG8gbm9uLWRhdGEgcGFydHMgb2Ygb3VyIGdyYXBoaWMuIFdoaWxlIGxhYnMoKSBpcyBmaW5lIGZvciBhZGRpbmcgbGFiZWxzIGFuZCBhIHRpdGxlLCB0aGVtZXMgYWxsb3cgdXMgdG8gY2hvb3NlIHRoZSBzaXplLCBmb250LCBjb2xvdXIsIHBvc2l0aW9uLCBldGMuIG9mIHRoYXQgdGV4dC4gVG9kYXkgSSdtIGdvaW5nIHRvIGludHJvZHVjZSB5b3UgdG8gc29tZSBjb21wbGV0ZSB0aGVtZXMuCgpMZXQncyBnbyBiYWNrIHRvIG91ciBuaWNlLCBjbGVhbi1sb29raW5nIGJveHBsb3RzOgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fYm94cGxvdChhZXMoZmlsbD10eXBlKSwgY29sb3VyPSJibGFjayIpCmBgYApXaGlsZSB0aGUgZ3JleSBiYWNrZ3JvbnVkIGlzIHRoZSBkZWZhdWx0IGluIGdncGxvdCwgaXQncyBjZXJ0YWlubHkgbm90IGEgcmVxdWlyZW1lbnQuIFRoYXQncyBvZnRlbiBvbmUgb2YgdGhlIGZpcnN0IHRoaW5ncyBJIGNoYW5nZSEKCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXR5cGUpLCBjb2xvdXI9ImJsYWNrIikrCiAgdGhlbWVfYncoKQpgYGAKVGhlbWVfYncoKSBpcyBhIGNvbXBsZXRlIHRoZW1lLCBtZWFuaW5nIHRoYXQgaXQncyBhIHRoZW1lIHRoYXQgY2FuIGJlIGFwcGxpZWQgYXMgYSBsYXllciB0aGF0IGNoYW5nZXMgdGhlIGxvb2sgb2YgeW91ciBvdmVyYWxsIHBsb3QuIFRoZW1lX2dyZSgpIGlzIHRoZSBkZWZhdWx0LiBZb3UgY2FuIGFsc28gdHJ5IG90aGVyIGNvbXBsZXRlIHRoZW1lcywgc3VjaCBhcyB0aGVtZV9kYXJrKCksIHRoZW1lX2xpZ2h0KCksIHRoZW1lX2NsYXNzaWMoKSwgdGhlbWVfdm9pZCgpIGFuZCBtb3JlLgoKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9dHlwZSksIGNvbG91cj0iYmxhY2siKSsKICB0aGVtZV9jbGFzc2ljKCkKYGBgClBlcnNvbmFsbHksIEkgbGlrZSB0aGlzIG9uZSBiZXN0IQoKCkRBWSAxMgoKT24gdGhlIHR3ZWxmdGggZGF5IG9mIENocmlzdG1hcy4uLgoKLi4uIHdlIHdvcmtlZCB3aXRoIHRoZW1lKCkgYW5kIG1vcmUgY29udHJvbCB3aXRoIG5vbi1kYXRhIHBhcnRzIG9mIG91ciBncmFwaGljcyEKCkhlcmUsIEknbSBrZWVwaW5nIHRoZW1lX2NsYXNzaWMoKSBhbmQgYnVpbGRpbmcgb24gdG9wIG9mIHRoYXQuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXR5cGUpLCBjb2xvdXI9ImJsYWNrIikrCiAgbGFicyh0aXRsZT0iQ2hyaXN0bWFzIFRyZWVzIiwgeD1OVUxMLCB5PSJUcmVlIGhlaWdodCIpKwogIHRoZW1lX2NsYXNzaWMoKSsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNiwgY29sb3VyID0gImdvbGQiKSwKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiLCBzaXplID0gNCwgY29sb3VyID0gInJlZCIpLAogICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDAsIDEpLAogICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoKSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChjb2xvdXI9ImRhcmtncmVlbiIpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKCkpCmBgYApJbiB0aGlzIGNhc2UsIEkgd2FudGVkIHRvIHNob3cgeW91IGhvdyB5b3UgY2FuIGtlZXAgdGhlIGdyaWQgbGluZXMsIHdoaWxlIGNoYW5naW5nIHRvIHRoZW1lX2NsYXNzaWMoKS4gV2UndmUgYWxzbyBjaGFuZ2VkIHRoZSBzdHlsZSBhbmQgc2l6ZSBvZiB0aGUgdGl0bGUsIG1vdmVkIHRoZSBsZWdlbmQgYW5kIGNoYW5nZWQgdGhlIGNvbG91cnMsIGNoYW5nZWQgdGhlIGNvbG91ciBvZiB0aGUgeSBheGlzIGxhYmVsLCBhbmQgY2hhbmdlZCB0aGUgc2l6ZSBvZiB0aGUgeCBheGlzIHRpY2sgbGFiZWxzLiBUaGlzIGlzIGJ5IG5vIG1lYW5zIGEgcHJldHR5IGdyYXBoaWMsIGJ1dCBob3BlZnVsbHkgaXQgZ2l2ZXMgeW91IGFuIGlkZWEgb2YgZGlmZmVyZW50IHdheXMgd2UgY2FuIGNoYW5nZSBmZWF0dXJlcyBvZiBvdXIgZ3JhcGhpYy4gVGhlcmUgYXJlIG1hbnkgbW9yZSBjaGFuZ2VzIHlvdSBjb3VsZCBtYWtlIGFuZCBsb3RzIG9mIGdyZWF0IG9ubGluZSB0dXRvcmlhbHMgdGhhdCBjb3ZlciB0aGlzIGluIG1vcmUgZGV0YWlsLgoKREFZIDEzCgpPbiB0aGUgdGhpcnRlZW50aCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi4gd2UgaW50cm9kdWNlZCBzY2FsZXMhCgpUbyBkbyB0aGlzLCB3ZSBuZWVkIHRvIGludHJvZHVjZSBzY2FsZXMuIFdlJ2xsIGJlIHdvcmtpbmcgd2l0aCBzY2FsZXMgZm9yIGEgZmV3IGRheXMgLSBnZXQgZXhjaXRlZCEgU2NhbGVzIGFsbG93IHVzIHRvIG92ZXJyaWRlIGRlZmF1bHRzLiBTaW1pbGFyIHRvIHRoZW1lcywgc2NhbGVzIGFsbG93IHVzIG1vcmUgY29udHJvbCBvdmVyIHdoYXQgb3VyIGdyYXBoaWMgbG9va3MgbGlrZSwgYnV0IHNjYWxlcyBmb2N1cyBvbiBjaGFuZ2luZyB0aGUgbG9vayBvZiB0aGUgZGF0YS4KCkxldCdzIHN0YXJ0IHdpdGggcG9zaXRpb24gc2NhbGVzLiBUaGUgbW9zdCBjb21tb25seSB1c2VkIGFyZSBzY2FsZV94X2NvbnRpbnVvdXMoKSBhbmQgc2NhbGVfeV9jb250aW51b3VzKCkuIFNpbmNlIHdlIGFyZSB3b3JraW5nIHdpdGggY2F0ZWdvcmljYWwgZGF0YSByaWdodCBub3cgKHRyZWUgdHlwZXMpLCB3ZSBjb3VsZCBzd2FwIG91dCBzY2FsZV94X2NvbnRpbnVvdXMoKSBmb3Igc2NhbGVfeF9kaXNjcmV0ZSgpLiBVc2luZyB0aGVzZSwgd2UgY2FuIHNldCB0aGUgbGltaXRzIG9mIG91ciBzY2FsZXMuIFdlIGRvbid0IG5lZWQgdG8gY2hhbmdlIHRoZSBsaW1pdHMgb2Ygb3VyIGRpc2NyZXRlICh4KSBheGlzLCBidXQgbGV0J3MgY2hhbmdlIHRoZSBsaW1pdHMgb2Ygb3VyIHkgYXhpcy4KCkhlcmUncyBvdXIgYm94cGxvdCBncmFwaGljLCBidXQgSSd2ZSBjaGFuZ2VkIG91ciB5IGF4aXMgdG8gQ2hyaXN0bWFzIG1hZ2ljIGluc3RlYWQgb2YgdHJlZSBoZWlnaHQ6CmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9eG1hcy5tYWdpYykpKwogIGdlb21fYm94cGxvdChhZXMoZmlsbD10eXBlKSwgY29sb3VyPSJibGFjayIpKwogIGxhYnModGl0bGU9IkNocmlzdG1hcyBUcmVlcyIsIHg9TlVMTCwgeT0iQ2hyaXN0bWFzIG1hZ2ljIikrCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKTm93IGxldCdzIHRyeSBjaGFuZ2luZyB0aGUgeS1heGlzIGxpbWl0czoKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT14bWFzLm1hZ2ljKSkrCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXR5cGUpLCBjb2xvdXI9ImJsYWNrIikrCiAgbGFicyh0aXRsZT0iQ2hyaXN0bWFzIFRyZWVzIiwgeD1OVUxMLCB5PSJDaHJpc3RtYXMgbWFnaWMiKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDAsMTUpKQpgYGAKTm90ZTogeW91IGNhbiBhbHNvIHVzZSBsaW1zKHg9YygjLCMpLCB5PWMoIywjKSkgKHJlcGxhY2luZyB0aGUgI3Mgd2l0aCB5b3VyIGRlc2lyZWQgbGltaXRzKS4gVGhpcyBpcyBzaW1wbGVyIGFuZCBmYXN0ZXIsIGJ1dCB3ZSB3aWxsIHN0aWNrIHdpdGggc2NhbGVfeV9jb250aW51b3VzKCkgYmVjYXVzZSB3ZSB3aWxsIG1ha2UgYWRkaXRpb25hbCBhZGp1c3RtZW50cyBiZWxvdy4KCldoZW4gd2UgaW5jcmVhc2VkIG91ciB5LWF4aXMgbGltaXRzLCBpdCBjaGFuZ2VzIHRoZSBsYWJlbHMgb2Ygb3VyIHktYXhpcyB0aWNrcyB0byBpbmNsdWRlIDAuNXMuIE1heWJlIHdlJ2QgcmF0aGVyIGhhdmUgd2hvbGUgbnVtYmVycyBvciBtYXliZSBqdXN0IGZld2VyIG51bWJlcnMgYWx0b2dldGhlci4gV2UgY2FuIHNwZWNpZnkgdGhpcyB1c2luZyB0aGUgc2FtZSBzY2FsZV95X2NvbnRpbnVvdXMoKSBidXQgYWRkaW5nICJicmVha3M9Ii4KCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9eG1hcy5tYWdpYykpKwogIGdlb21fYm94cGxvdChhZXMoZmlsbD10eXBlKSwgY29sb3VyPSJibGFjayIpKwogIGxhYnModGl0bGU9IkNocmlzdG1hcyBUcmVlcyIsIHg9TlVMTCwgeT0iQ2hyaXN0bWFzIG1hZ2ljIikrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygyLDEyKSwgYnJlYWtzPWMoMiwgNywgMTIpKQpgYGAKSWYgd2UgZG9uJ3Qgd2FudCBhbnkgYnJlYWtzIHdlIGNhbiBzcGVjaWZ5IGJ5IHVzaW5nICJicmVha3M9TlVMTCIuCgpGaW5hbGx5LCB3ZSBjYW4gbW9kaWZ5IHRoZSBheGlzIHRpY2sgbGFiZWxzLiBMZXQncyBjaGFuZ2UgdGhlIG5hbWVzIG9mIG91ciB4LWF4aXMgdGljayBsYWJlbHMuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9eG1hcy5tYWdpYykpKwogIGdlb21fYm94cGxvdChhZXMoZmlsbD10eXBlKSwgY29sb3VyPSJibGFjayIpKwogIGxhYnModGl0bGU9IkNocmlzdG1hcyBUcmVlcyIsIHg9TlVMTCwgeT0iQ2hyaXN0bWFzIG1hZ2ljIikrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygyLDEyKSwgYnJlYWtzPWMoMiwgNywgMTIpKSsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKCJCYWxzYW0gRmlyIiwgIkphY2sgUGluZSIsICJCbHVlIFNwcnVjZSIpKQpgYGAKVGhlcmUncyBtdWNoIG11Y2ggbW9yZSB0aGF0IGNhbiBiZSBkb25lIHdpdGggcG9zaXRpb24gc2NhbGVzLiBJIHN1Z2dlc3QgdGFraW5nIGEgbG9vayBhdCBDaC4gMTAgb2YgdGhpcyBib29rIGJ5IEhhZGxleSBXaWNraGFtLCBEYW5pZWxsZSBOYXZhcnJvLCBhbmQgVGhvbWFzIExpbiBQZWRlcnNlbiwgd2hpY2ggSSByZWxpZWQgb24gaGVhdmlseSBmb3IgdGhpcyBzZWN0aW9uLiBodHRwczovL2dncGxvdDItYm9vay5vcmcvc2NhbGUtcG9zaXRpb24uaHRtbCNzY2FsZS1wb3NpdGlvbgoKREFZIDE0CgpPbiB0aGUgZm91cnRlZW50aCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi4gd2Ugd29ya2VkIHdpdGggY29sb3VyIHNjYWxlcyEKCldlJ3ZlIGJlZW4gc2VlaW5nIHRoZSBzYW1lIHRocmVlIGNvbG91cnMgb3ZlciBhbmQgb3ZlcjogcmVkLCBncmVlbiwgYmx1ZS4gQnV0IHdoYXQgaWYgd2Ugd2FudCB0byBzcGVjaWZ5IHRoZSBjb2xvdXJzIHdoZW4gd2UgY2hvb3NlIGNvbG91ciBvciBmaWxsIGJ5IHR5cGU/IFRoaXMgaXMgd2hlcmUgY29sb3VyIHNjYWxlcyBjb21lIGluLgoKV2UgY2FuIGNob29zZSBhIGRpZmZlcmVudCBjb2xvdXIgcGFsZXR0ZXMgYnkgaW5zdGFsbGluZyBjb2xvdXIgcGFsZXR0ZSBwYWNrYWdlcyBhbmQgbG9hZGluZyB0aGVtLgoKSGVyZSBpcyBhIGdvb2QgcmVzb3VyY2UsIFJDb2xvckJyZXdlci4KYGBge3J9Cmluc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpgYGAKCkxldCdzIGNob29zZSBhIHBhbGV0dGUgZnJvbSBSQ29sb3JCcmV3ZXIuIFlvdSBjYW4gZmluZCB0aGUgbmFtZXMgYW5kIHBhbGV0dGVzIGhlcmU6IGh0dHBzOi8vci1ncmFwaC1nYWxsZXJ5LmNvbS8zOC1yY29sb3JicmV3ZXJzLXBhbGV0dGVzLmh0bWwgb3IgeW91IGNhbiBydW4gdGhpcyBjb2RlIHRvIGRpc3BsYXkgdGhlbSBpbiBSOgpgYGB7cn0KUkNvbG9yQnJld2VyOjpkaXNwbGF5LmJyZXdlci5hbGwoKQpgYGAKSGVyZSB3ZSdsbCBjcmVhdGUgb3VyIGJveHBsb3RzIGFnYWluLCBzbyB3ZSB3aWxsIHdhbnQgdG8gY2hhbmdlIHRoZSAiZmlsbCIgcmF0aGVyIHRoYW4gdGhlICJjb2xvdXIiLCB0aGVyZWZvcmUgd2UgdXNlIHNjYWxlX2ZpbGxfYnJld2VyKCkuIFdlJ3JlIGdvaW5nIHRvIHRyeSB0aGUgRGFyazIgcGFsZXR0ZToKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9dHlwZSksIGNvbG91cj0iYmxhY2siKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iRGFyazIiKQpgYGAKCldoYXQgaWYgeW91IGxpa2UgdGhlIHBhbGV0dGUgYnV0IGRvbid0IGxpa2UgaG93IGdncGxvdCBhcHBsaWVkIHRoZSBjb2xvdXJzPyBPciBtYXliZSB5b3UgY2FuJ3QgZmluZCB0aGUgcGVyZmVjdCBwYWxldHRlIGFuZCB3YW50IHRvIGNyZWF0ZSB5b3VyIG93bj8gV2UgY2FuIG1hbnVhbGx5IGFzc2lnbiBjb2xvdXJzIHRvby4uLiBidXQgd2UnbGwgd2FpdCB1bnRpbCB0b21vcnJvdyBmb3IgdGhhdCBvbmUhCgpEQVkgMTUKCk9uIHRoZSBmaWZ0ZWVudGggZGF5IG9mIENocmlzdG1hcy4uLgoKLi4ud2UgbGVhcm5lZCBob3cgdG8gYXNzaWduIGNvbG91cnMgbWFudWFsbHkhCgpGb3IgdGhpcyB3ZSB1c2Ugc2NhbGVfY29sb3JfbWFudWFsKCkgYW5kL29yIHNjYWxlX2ZpbGxfbWFudWFsKCkuIEZvciB0aGUgYm94cGxvdHMsIHdlIHdpbGwgdXNlIHNjYWxlX2ZpbGxfbWFudWFsKCkuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXR5cGUpLCBjb2xvdXI9ImJsYWNrIikrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJkYXJrZ3JlZW4iLCAiZmlyZWJyaWNrMiIsICJtZWRpdW1zZWFncmVlbiIpKQpgYGAKVGhlIGNvbG91cnMgd2lsbCBiZSBhc3NpZ25lZCBpbiB0aGUgb3JkZXIgd2UgZ2F2ZSB0aGVtLCBzbyB5b3UgY2FuIGFsc28gcmVwZWF0IGEgY29sb3VyIChlLmcuLCBncmVlbiwgcmVkLCBncmVlbikgYW5kIGl0IHdpbGwgYmUgYXNzaWduZWQgaW4gdGhhdCBvcmRlci4gSW5zdGVhZCBvZiBjb2xvdXIgbmFtZXMsIHlvdSBjYW4gYWxzbyB1c2UgdGhlIGNvbG9yIGNvZGVzIChlLmcuLCAjRTY5RjAwKS4KCldlJ2xsIGJlIHVzaW5nIHRoZXNlIGNvbG91cnMgYWdhaW4gYW5kIGFnYWluLCBzbyB3aHkgZG9uJ3Qgd2Ugc2F2ZSB0aGVtIGFzIGEgdmVjdG9yPwpgYGB7cn0KeG1hcyA8LWMoImRhcmtncmVlbiIsICJmaXJlYnJpY2syIiwgIm1lZGl1bXNlYWdyZWVuIikKYGBgCgoKWW91IGNhbiBhbHNvIHNwZWNpZmljIHRoZSBjb2xvdXJzIHRoYXQgd2lsbCBiZSBhc3NpZ25lZCB0byBlYWNoIGxldmVsIG9mIHlvdXIgdmFyaWFibGU6CmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXR5cGUpLCBjb2xvdXI9ImJsYWNrIikrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz14bWFzKQpgYGAKCkNocmlzdG1hcyBjb2xvdXJzIGFyZW4ndCBuZWNlc3NhcmlseSBjb2xvdXItYmxpbmQgZnJpZW5kbHkuIFRoZXJlIGFyZSBsb3RzIG9mIGZhbnRhc3RpYyByZXNvdXJjZXMgd2hlbiBjb25zaWRlcmluZyBjb2xvdXItYmxpbmQgZnJpZW5kbHkgcGFsZXR0ZXMgZm9yIHlvdXIgZ3JhcGhpY3MuIEhlcmUncyBvbmU6IGh0dHBzOi8vY29sb3JicmV3ZXIyLm9yZy8jdHlwZT1zZXF1ZW50aWFsJnNjaGVtZT1ZbEduQnUmbj05CgpEQVkgMTYKCk9uIHRoZSBzaXh0ZWVudGggZGF5IG9mIENocmlzdG1hcy4uLgoKLi4ud2Ugd29ya2VkIHdpdGggYSBkaWZmZXJlbnQgdHlwZSBvZiBwbG90IGFuZCBhZGRlZCBtdWx0aXBsZSBnZW9tcyB0byBvbmUgcGxvdCEKCldlIGhhdmVuJ3QgZXZlbiB0b3VjaGVkIHRoZSBzbGVpZ2ggZGF0YXNldCEgTGV0J3MgY3JlYXRlIGEgZ3JhcGhpYyB3aXRoIHRoYXQgc28gd2UgY2FuIHdvcmsgd2l0aCBhIGRpZmZlcmVudCB0eXBlIG9mIHBsb3QuCmBgYHtyfQpzbGVpZ2hzICU+JQogIGdncGxvdChhZXMoeD1kZWVycG93ZXIsIHk9a21fcGVyX2NhcnJvdCkpKwogIGdlb21fcG9pbnQoYWVzKGZpbGw9bmFtZSksIHNoYXBlPTIxLCBzaXplPTMpKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKSGVyZSB3ZSd2ZSB0b2xkIGdncGxvdCB0byBjb2xvdXIgZWFjaCBzbGVpZ2ggdHlwZSBhIGRpZmZlcmVudCBjb2xvdXIgKHNvbWUgYXJlIG5lYXJseSBpbXBvc3NpYmxlIHRvIGRpZmZlcmVudGlhdGUgYnV0IHdlIHdvbid0IHdvcnJ5IGFib3V0IHRoYXQgdG9kYXkpLgoKTWF5YmUgd2Ugd2FudCB0byBhZGQgYSB0cmVuZGxpbmUgdG8gb3VyIHBsb3QuIFdlIGNhbiBkbyB0aGlzIGJ5IGFkZGluZyBhIGdlb21fc21vb3RoKCkgbGF5ZXI6CmBgYHtyfQpzbGVpZ2hzICU+JQogIGdncGxvdChhZXMoeD1kZWVycG93ZXIsIHk9a21fcGVyX2NhcnJvdCkpKwogIGdlb21fcG9pbnQoYWVzKGZpbGw9bmFtZSksIHNoYXBlPTIxLCBzaXplPTMpKwogIGdlb21fc21vb3RoKCkrCiAgdGhlbWVfY2xhc3NpYygpCmBgYApBbmQgbWF5YmUgd2Ugd2FudCB0byBjdXN0b21pemUgdGhhdCBsaW5lLiBUaGUgZGVmYXVsdCBpcyBhIGJsdWUgbGluZSB3aXRoIGdyZXkgYmFja2dyb3VuZC4gV2UgY2FuIGNoYW5nZSB0aGF0IHRvIGEgYmxhY2sgbGluZSB1c2luZyAiY29sb3VyID0iIGFuZCB3ZSBjYW4gY2hhbmdlIHRoZSB0cmFuc3BhcmVuY3kgb2YgdGhlIGVycm9yIHVzaW5nICJhbHBoYSA9Ii4gV2UgY2FuIGFsc28gY2hhbmdlIHRoZSBtZXRob2Qgb2YgaG93IHRoZSBsaW5lIGlzIGNhbGN1bGF0ZWQgdXNpbmcgIm1ldGhvZCA9IgpgYGB7cn0Kc2xlaWdocyAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGVlcnBvd2VyLCB5PWttX3Blcl9jYXJyb3QpKSsKICBnZW9tX3BvaW50KGFlcyhmaWxsPW5hbWUpLCBzaGFwZT0yMSwgc2l6ZT0zKSsKICBnZW9tX3Ntb290aChjb2xvdXI9ImJsYWNrIiwgYWxwaGE9MC4yLCBtZXRob2Q9bG0pKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKQWxwaGEgY2FuIGFsc28gYmUgaGVscGZ1bCB3aGVuIHlvdSBoYXZlIG92ZXJsYXBwaW5nIHBvaW50cy4KYGBge3J9CnNsZWlnaHMgJT4lCiAgZ2dwbG90KGFlcyh4PWRlZXJwb3dlciwgeT1rbV9wZXJfY2Fycm90KSkrCiAgZ2VvbV9wb2ludChhZXMoZmlsbD1uYW1lKSwgc2hhcGU9MjEsIHNpemU9MywgYWxwaGE9MC41KSsKICBnZW9tX3Ntb290aChjb2xvdXI9ImJsYWNrIiwgYWxwaGE9MC4yKSsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCk1vcmUgZGV0YWlsZWQgYW5kIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gb24gY29sb3VyIHNjYWxlcyBjYW4gYmUgZm91bmQgaGVyZTogaHR0cHM6Ly9nZ3Bsb3QyLWJvb2sub3JnL3NjYWxlLWNvbG91ci5odG1sCgoKREFZIDE3CgpPbiB0aGUgc2V2ZW50ZWVudGggZGF5IG9mIENocmlzdG1hcy4uLgoKLi4ud2UgZWRpdGVkIG91ciBsZWdlbmQhCgpXYW50IHRvIG1vdmUgeW91ciBsZWdlbmQ/IENoYW5nZSB0aGUgc2hhcGUsIHN0eWxlLCBzaXplPyBHZXQgcmlkIG9mIGl0IGNvbXBsZXRlbHk/CgpMZXQncyBnbyBiYWNrIHRvIG91ciBib3hwbG90cy5XZSBjYW4gcmVtb3ZlIHRoZSBsZWdlbmQgdXNpbmcgInNob3cubGVnZW5kID0gRkFMU0UiLiBXZSBwdXQgdGhpcyBpbiB0aGUgZ2VvbV9ib3hwbG90KCkgc2luY2UgdGhlIGxlZ2VuZCBpcyBsaW5rZWQgdG8gdGhhdCBsYXllci4KYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9dHlwZSksIGNvbG91cj0iYmxhY2siLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPXhtYXMpCmBgYApBbm90aGVyIG9wdGlvbiBpcyB0byB1c2UgZ3VpZGVzKCkuIEluIHRoaXMgY2FzZSB3ZSB1c2UgZmlsbD0ibm9uZSIgYnV0IGlmIHdlIHdlcmUgd29ya2luZyB3aXRoIGNvbG91ciBpbnN0ZWFkIG9mIGZpbGwsIHdlIHdvdWxkIHR5cGUgY29sb3VyPSJub25lIi4gVGhpcyB3YXkgb2YgcmVtb3ZpbmcgdGhlIGxlZ2VuZCBpcyBoYW5keSBpbiBpbnN0YW5jZXMgd2hlbiB5b3UgaGF2ZSBtdWx0aXBsZSBnZW9tcy4gV2UgY2FuIGFkZCBvbmUgbGl0dGxlIGxpbmUgb2YgY29kZSB0byByZW1vdmUgdGhlIGxlZ2VuZCwgaW5zdGVhZCBvZiB0eXBpbmcgInNob3cubGVnZW5kPUYiIGludG8gZWFjaCBnZW9tIGxheWVyLgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fYm94cGxvdChhZXMoZmlsbD10eXBlKSwgY29sb3VyPSJibGFjayIpKwogIHRoZW1lX2NsYXNzaWMoKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9eG1hcykrCiAgZ3VpZGVzKGZpbGw9Im5vbmUiKQpgYGAKCldlIGNhbiBhbHNvIGNoYW5nZSB0aGUgdGV4dCBvZiB0aGUgbGVnZW5kLiBXaGVuIHdlIGNoYW5nZWQgdGhlIHgtYXhpcyBsYWJlbHMsIHRoZSBsZWdlbmQgZGlkbid0IGNoYW5nZSB3aXRoIGl0LiBMZXQncyBmaXggdGhhdCBub3cuIFdlIGRvIHRoaXMgYnkgYWRkaW5nIHRoZSBzYW1lICJsYWJlbHMgPSBjKC4uLiIgaW4gc2NhbGVfZmlsbF9tYW51YWwoKSBhcyB3ZWxsIGFzIHNjYWxlX3hfZGlzY3JldGUoKS4gV2h5PyBCZWNhdXNlIHNjYWxlX2ZpbGxfbWFudWFsKCkgcmVmZXJzIHRvIHRoZSBjb2xvdXJzIG9mIHlvdXIgZGF0YSwgYW5kIHRoZSBsZWdlbmQgcmVwcmVzZW50cyB0aGF0ICh0aGV5IGFyZSBkaXJlY3RseSBsaW5rZWQpLiBzY2FsZV94X2Rpc2NyZXRlKCkgaXMgZm9jdXNlZCBzb2xlbHkgb24gdGhlIHgtYXhpcy4KYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9dHlwZSksIGNvbG91cj0iYmxhY2siKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVHJlZSBUeXBlIiwgdmFsdWVzPXhtYXMsIGxhYmVscz1jKCJCYWxzYW0gRmlyIiwgIkphY2sgUGluZSIsICJCbHVlIFNwcnVjZSIpKSsKICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIkJhbHNhbSBGaXIiLCAiSmFjayBQaW5lIiwgIkJsdWUgU3BydWNlIikpCmBgYAoKKlJFRlJFU0hFUiogV2hhdCBpZiB3ZSB3YW50IHRvIGNoYW5nZSB0aGUgcG9zaXRpb24gb2YgdGhlIGxlZ2VuZCBvciB0aGUgY29sb3VyIG9mIHRoZSB0ZXh0Pz8gUmVtZW1iZXIgYmFjayB0byB3aGVuIHdlIHRhbGtlZCBhYm91dCB0aGVtZXM/IElmIHdlIHdhbnQgdG8gbWFrZSBjaGFuZ2VzIHRvIGFueXRoaW5nIHRoYXQncyBub3QgcmVsYXRlZCB0byB0aGUgZGF0YSAoaS5lLiwgaXQgY291bGQgYmUgYSBwbG90IG9mIGFueXRoaW5nIG9yIG9uZSB3aXRob3V0IGFueSBkYXRhIGluIGl0KSwgd2UgdXNlIFRIRU1FUy4KCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXR5cGUpLCBjb2xvdXI9ImJsYWNrIikrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlRyZWUgVHlwZSIsIHZhbHVlcz14bWFzLCBsYWJlbHM9YygiQmFsc2FtIEZpciIsICJKYWNrIFBpbmUiLCAiQmx1ZSBTcHJ1Y2UiKSkrCiAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKCJCYWxzYW0gRmlyIiwgIkphY2sgUGluZSIsICJCbHVlIFNwcnVjZSIpKSsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gImdvbGRlbnJvZDMiLCBzaXplID0xNCwgZmFjZT0iYm9sZCIpLCBsZWdlbmQucG9zaXRpb249InRvcCIpCmBgYAoKREFZIDE4CgpPbiB0aGUgZWlnaHRlZW50aCBkYXkgb2YgQ2hSaXN0bWFzLi4uCgouLi4gd2UgbGVhcm5lZCBhYm91dCBndWlkZXMhCgpZZXN0ZXJkYXksIHdlIG1hZGUgc29tZSBlZGl0cyB0byBvdXIgbGVnZW5kIHVzaW5nIHNjYWxlcyBhbmQgdGhlbWVzLiBUb2RheSwgd2Ugd2lsbCBpbnRyb2R1Y2Ugb25lIG1vcmUgd2F5IHRvIGV4ZXJjaXNlIG1vcmUgZmluZSBjb250cm9sIG92ZXIgeW91ciBncmFwaGljczogZ3VpZGVzKCkgYW5kIGd1aWRlXyBmdW5jdGlvbnMhIEd1aWRlcywgbGlrZSBvdXIgbGVnZW5kcyBhbmQgYXhlcywgaGVscCB1cyBvciBvdXIgYXVkaWVuY2UgaW50ZXJwcmV0IG91ciBwbG90cy4gV2UgY2FuIHVzZSBndWlkZXMoKSBvciB0aGUgZ3VpZGVfIGFyZ3VtZW50IF8qKCkgZnVuY3Rpb25zIHRvIG1ha2UgYWRkaXRpb25hbCBjaGFuZ2VzIHRvIG91ciBsZWdlbmRzIGFuZCBheGVzLiBIZXJlJ3MgYSBncmVhdCBleHBsYW5hdGlvbiBvZiBzY2FsZXMgYW5kIGd1aWRlcyBmcm9tIENoLjE1IG9mIFdpY2toYW0sIE5hdmFycm8gYW5kIFBlZGVyc29uJ3MgYm9vaywgd2hpY2ggSSBoaWdobHkgcmVjb21tZW5kIHlvdSBjaGVjayBvdXQ6IGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy9pbmRleC5odG1sCgoiRm9ybWFsbHksIGVhY2ggc2NhbGUgaXMgYSBmdW5jdGlvbiBmcm9tIGEgcmVnaW9uIGluIGRhdGEgc3BhY2UgKHRoZSBkb21haW4gb2YgdGhlIHNjYWxlKSB0byBhIHJlZ2lvbiBpbiBhZXN0aGV0aWMgc3BhY2UgKHRoZSByYW5nZSBvZiB0aGUgc2NhbGUpLiBUaGUgYXhpcyBvciBsZWdlbmQgaXMgdGhlIGludmVyc2UgZnVuY3Rpb24sIGtub3duIGFzIHRoZSBndWlkZTogaXQgYWxsb3dzIHlvdSB0byBjb252ZXJ0IHZpc3VhbCBwcm9wZXJ0aWVzIGJhY2sgdG8gZGF0YS4gWW91IG1pZ2h0IGZpbmQgaXQgc3VycHJpc2luZyB0aGF0IGF4ZXMgYW5kIGxlZ2VuZHMgYXJlIHRoZSBzYW1lIHR5cGUgb2YgdGhpbmcsIGJ1dCB3aGlsZSB0aGV5IGxvb2sgdmVyeSBkaWZmZXJlbnQgdGhleSBoYXZlIHRoZSBzYW1lIHB1cnBvc2U6IHRvIGFsbG93IHlvdSB0byByZWFkIG9ic2VydmF0aW9ucyBmcm9tIHRoZSBwbG90IGFuZCBtYXAgdGhlbSBiYWNrIHRvIHRoZWlyIG9yaWdpbmFsIHZhbHVlcy4iCgpMZXQncyBsb29rIGF0IHNvbWUgZXhhbXBsZXMuIExldCdzIHRyeSBhIG5ldyBncmFwaGljIHdpdGggb3VyIGRhdGEsIGFuZCB3ZSdsbCB1c2UgYSBncmFkaWVudCBjb2xvdXIgc2NhbGUgdG8gY29sb3VyIG91ciBwb2ludHMgYmFzZWQgb24gdGhlIGFtb3VudCBvZiAiQ2hyaXN0bWFzIG1hZ2ljIiBpbiBvdXIgdHJlZXM6CmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PW5lZWRsZS5kcm9wLCB5PWhlaWdodCkpKwogIGdlb21fcG9pbnQoYWVzKGNvbG91cj14bWFzLm1hZ2ljKSwgc2l6ZT0yKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfY29sb3VyX2NvbnRpbnVvdXMobG93PSJyZWQiLCBoaWdoPSJtZWRpdW1zZWFncmVlbiIpCmBgYAoKQmVmb3JlIHdlIGdldCBiYWNrIHRvIGd1aWRlcywgbGV0J3MgcXVpa2NseSBjaGF0IGFib3V0IHRoZSBncmFkaWVudCBzY2FsZS4gVGhlcmUgYXJlIG1hbnksIG1hbnkgd2F5cyB5b3UgY2FuIGVkaXQgdGhlIGNvbG91cnMsIGJ1dCBpbiB0aGlzIGNhc2Ugd2UgdG9sZCBnZ3Bsb3QgdGhhdCB3ZSB3YW50ZWQgdG8gY2hhbmdlIHRoZSBjb2xvdXIgb2Ygb3VyIHBvaW50cyB3aXRoIGEgZ3JhZGllbnQgInNjYWxlX2NvbG91cl9jb250aW51b3VzKCkiIGFuZCB0aGVuIHdlIHNldCB0aGUgaGlnaCBhbmQgbG93IGNvbG91cnMuIFdlIGNvdWxkIGhhdmUgYWxzbyBzZXQgdGhlIG1pZGRsZSBjb2xvdXIgb3IgY2hvc2VuIGFuIGV4aXN0aW5nIGdyYWRpZW50LiBMZWFybiBtb3JlIGhlcmU6IGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy9zY2FsZS1jb2xvdXIuaHRtbAoKQmFjayB0byBndWlkZXMhIChjb2xvdXJzIGFyZSBqdXN0IHNvIGRpc3RyYWN0aW5nISEpCgpXZSBjYW4gbWFrZSBhZGRpdGlvbmFsIGVkaXRzIHRvIG91ciBsZWdlbmRzIHVzaW5nICIrIGd1aWRlcygpIiBvciBieSBzcGVjaWZ5aW5nIHRoZSAiZ3VpZGUgPSAiIGFyZ3VtZW50IHdpdGhpbiBvdXIgc2NhbGUgbGF5ZXIgKHNjYWxlX2NvbG91cl9jb250aW51b3VzKCksIHdoaWNoIGNvcnJlc3BvbmRzIHdpdGggb3VyIGxlZ2VuZCkuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PW5lZWRsZS5kcm9wLCB5PWhlaWdodCkpKwogIGdlb21fcG9pbnQoYWVzKGNvbG91cj14bWFzLm1hZ2ljKSwgc2l6ZT0yKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfY29sb3VyX2NvbnRpbnVvdXMobG93PSJyZWQiLCBoaWdoPSJtZWRpdW1zZWFncmVlbiIpKwogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfY29sb3VyYmFyKHJldmVyc2U9VFJVRSwgZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBiYXJoZWlnaHQ9dW5pdCgyLCAiY20iKSkpCmBgYApIZXJlIHdlJ3ZlIGZsaXBwZWQgb3VyIGJhciBob3Jpem9udGFsbHkgYW5kIGluY3JlYXNlZCB0aGUgc2l6ZSBvZiB0aGUgbGVnZW5kLiBObyBjaGFuZ2VzIGhhdmUgYmVlbiBtYWRlIHRvIHRoZSByZXN0IG9mIG91ciBncmFwaGljLiBXZSBjYW4gYWNoaWV2ZSB0aGUgZXhhY3Qgc2FtZSBvdXRwdXQgYnkgYWRkaW5nICJndWlkZSA9ICIgdG8gb3VyIHNjYWxlX2NvbG91cl9jb250aW51b3VzKCkgbGF5ZXIuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PW5lZWRsZS5kcm9wLCB5PWhlaWdodCkpKwogIGdlb21fcG9pbnQoYWVzKGNvbG91cj14bWFzLm1hZ2ljKSwgc2l6ZT0yKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfY29sb3VyX2NvbnRpbnVvdXMobG93PSJyZWQiLCBoaWdoPSJtZWRpdW1zZWFncmVlbiIsIGd1aWRlID0gZ3VpZGVfY29sb3VyYmFyKHJldmVyc2U9VFJVRSwgZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBiYXJoZWlnaHQ9dW5pdCgyLCAiY20iKSkpCmBgYApIZXJlIGFyZSBhIGNvdXBsZSBtb3JlIHdheXMgd2UgY2FuIHVzZSBndWlkZXMgdG8gZWRpdCBvdXIgbGVnZW5kLiBMZXQncyBjaGFuZ2UgdXAgb3VyIHBsb3QgYSBiaXQuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXhtYXMubWFnaWMsIHk9bmVlZGxlLmRyb3ApKSsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXI9dHlwZSksIHNpemU9Mi41KSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9eG1hcykKYGBgClRoZXJlJ3MgYSBiaXQgb2Ygb3ZlcmxhcCBpbiBvdXIgcG9pbnRzLCBzbyBsZXQncyBhZGp1c3QgdGhlIHRyYW5zcGFyZW5jeSB1c2luZyBhbHBoYToKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9eG1hcy5tYWdpYywgeT1uZWVkbGUuZHJvcCkpKwogIGdlb21fcG9pbnQoYWVzKGNvbG91cj10eXBlKSwgc2l6ZT0yLjUsIGFscGhhPTAuNykrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPXhtYXMpCmBgYApCdXQgbWF5YmUgd2UgZG9uJ3Qgd2FudCBvdXIgbGVnZW5kIHRvIGFsc28gaGF2ZSB0cmFuc3BhcmVudCBwb2ludHMsIHNvIHdlIGNhbiB1c2UgYSBndWlkZSB0byBvdmVycmlkZSB0aGlzIGFlc3RoZXRpYyBjaGFuZ2UuCgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD14bWFzLm1hZ2ljLCB5PW5lZWRsZS5kcm9wKSkrCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyPXR5cGUpLCBzaXplPTIuNSwgYWxwaGE9MC43KSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9eG1hcykrCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzPWxpc3QoYWxwaGE9MSkpKQpgYGAKRmluYWxseSwgbGV0J3MgdXNlIGd1aWRlcyB0byBjaGFuZ2UgdGhlIGFlc3RoZXRpY3Mgb2Ygb3VyIGF4ZXMuIFdlIHdpbGwgZ28gYmFjayB0byBvdXIgc2xlaWdocyBkYXRhc2V0IGZvciB0aGlzIG9uZS4KYGBge3J9CnNsZWlnaHMgJT4lCmdncGxvdChhZXMoeD1uYW1lLCB5PWRlZXJwb3dlcikpKwogIGdlb21fcG9pbnQoKSsKICBsYWJzKHg9IlNsZWlnaHMiKSsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCkFzIHlvdSBjYW4gc2VlLCB0aGUgbmFtZXMgb2YgdGhlIHNsZWlnaHMgYXJlIGltcG9zc2libGUgdG8gcmVhZC4gTGV0J3MgZmxpcCB0aGUgbGFiZWxzIGF0IHRoZSBib3R0b20gc28gdGhhdCB0aGV5IHJ1biB2ZXJ0aWNhbGx5IGluc3RlYWQuCmBgYHtyfQpzbGVpZ2hzICU+JQpnZ3Bsb3QoYWVzKHg9bmFtZSwgeT1kZWVycG93ZXIpKSsKICBnZW9tX3BvaW50KCkrCiAgdGhlbWVfY2xhc3NpYygpKwogIGxhYnMoeD0iU2xlaWdocyIpKwogIGd1aWRlcyh4PWd1aWRlX2F4aXMoYW5nbGU9OTApKQpgYGAKTXVjaCBiZXR0ZXIhIEhvcGVmdWxseSBub3cgeW91IGhhdmUgYW4gaWRlYSBvZiBzb21lIG9mIHRoZSB3YXlzIHlvdSBjYW4gZWRpdCB5b3VyIGd1aWRlcyAobGVnZW5kcywgYXhlcykgdXNpbmcgZ3VpZGVzKCkgYW5kIGd1aWRlID0uCgpEYXkgMTkKCk9uIHRoZSBuaW5ldGVlbnRoIGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLiB3ZSBtYWRlIHBvc2l0aW9uIGFkanVzdG1lbnRzIQoKUG9zaXRpb24gYWRqdXN0bWVudHMgYXJlIGhhbmR5IGlmIHlvdSBoYXZlIG92ZXJsYXBwaW5nIGdlb21zIG9yIGRhdGEuIFlvdSBjYW4gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgdXNpbmcgdGhlIHBvc2l0aW9uIGFyZ3VtZW50IGluIHRoZSBnZW9tXygpIGZ1bmN0aWlvbnMuCgpJbnN0ZWFkIG9mIGJveHBsb3RzLCBsZXQncyBsb29rIGF0IHRoZSByYXcgZGF0YSBwb2ludHMgdXNpbmcgYSBkaWZmZXJlbnQgdHlwZSBvZiBnZW9tIGxhdGVyLCBnZW9tX2ppdHRlcigpLgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21faml0dGVyKGFlcyhjb2xvdXI9dHlwZSkpKwogIHRoZW1lX2NsYXNzaWMoKSsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz14bWFzKQoKYGBgClBvc2l0aW9uIGFkanVzdG1lbnRzIGNvbWUgaW4gaGFuZHkgd2l0aCBwb2ludCBkYXRhIGxpa2UgdGhpcywgbW9yZSBzbyB3aGVuIHdlJ3JlIHdvcmtpbmcgd2l0aCBsYXJnZSBkYXRhc2V0cyB0aGF0IGhhdmUgbWFueSBwb2ludHMuIExldCdzIGFkanVzdCB0aGUgcG9zaXRpb24gb2Ygb3VyIGppdHRlcmVkIHBvaW50cy4KYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX2ppdHRlcihhZXMoY29sb3VyPXR5cGUpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMDUsIGhlaWdodCA9IDAuNSkpKwogIHRoZW1lX2NsYXNzaWMoKSsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz14bWFzKQpgYGAKTGV0J3MgbG9vayBhdCBhbm90aGVyIGV4YW1wbGUuIFdlJ2xsIGJyaW5nIGJhY2sgb3VyIHNjYXR0ZXJwbG90IG9mIHNsZWlnaCBkYXRhIGJ1dCBJJ20gZ29pbmcgdG8gY3V0IGl0IGRvd24gYSBiaXQgdG8gbWFrZSBpdCBhIGVhc2llciB0byB3b3JrIHdpdGguIEknbGwgZG8gdGhpcyB1c2luZyBhbm90aGVyIHRpZHl2ZXJzZSBwYWNrYWdlLCBkcGx5ci4KCmBgYHtyfQpzbGVpZ2hzLnRyYW5zcG9zZWQgPC10KHNsZWlnaHMpCnNsZWlnaHMuc3Vic2V0IDwtc2xpY2Uoc2xlaWdocywgNDoxMikKYGBgCgpgYGB7cn0Kc2xlaWdocy5zdWJzZXQgJT4lCiAgZ2dwbG90KGFlcyh4PWRlZXJwb3dlciwgeT1rbV9wZXJfY2Fycm90KSkrCiAgZ2VvbV9wb2ludChhZXMoZmlsbD1uYW1lKSwgc2hhcGU9MjEsIHNpemU9MykrCiAgdGhlbWVfY2xhc3NpYygpCmBgYApUaGF0IGxlZ2VuZCBpcyBmaW5lLCBidXQgbGV0J3MgZ2V0IHJpZCBvZiBpdCBhbmQgaW5zdGVhZCBsYWJlbCBlYWNoIHBvaW50LkRvIHlvdSByZW1lbWJlciBob3cgdG8gcmVtb3ZlIHRoZSBsZWdlbmQ/CmBgYHtyfQpzbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGVlcnBvd2VyLCB5PWttX3Blcl9jYXJyb3QpKSsKICBnZW9tX3BvaW50KGFlcyhmaWxsPW5hbWUpLCBzaGFwZT0yMSwgc2l6ZT0zLCBzaG93LmxlZ2VuZCA9IEYpKwogIGdlb21fdGV4dChhZXMobGFiZWw9bmFtZSkpKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKV2VsbCB0aGlzIG1pZ2h0IHdvcmsgYmV0dGVyLCBidXQgdGhlIGxhYmVscyBhcmUgYWxsIG92ZXJsYXBwaW5nIGFuZCBkaWZmaWN1bHQgdG8gcmVhZC4gVGhpcyBpcyB3aGVyZSBwb3NpdGlvbl9udWRnZSgpIGNvbWVzIGluIGhhbmR5IQoKYGBge3J9CnNsZWlnaHMuc3Vic2V0ICU+JQogIGdncGxvdChhZXMoeD1kZWVycG93ZXIsIHk9a21fcGVyX2NhcnJvdCkpKwogIGdlb21fcG9pbnQoYWVzKGZpbGw9bmFtZSksIHNoYXBlPTIxLCBzaXplPTMsIHNob3cubGVnZW5kID0gRikrCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1uYW1lKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4PTEwLCB5PTAuNCkpKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKQW5kIGJlY2F1c2Ugb3VyIFN0ZWFsdGggU2xlaWdoIGlzIG9mZiB0aGUgcGxvdCwgbGV0J3MgZml4IHRoYXQgdXNpbmcgd2hhdCB3ZSBsZWFybmVkIG9uIERheSAxMyBhYm91dCBsaW1pdHMuCmBgYHtyfQpzbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGVlcnBvd2VyLCB5PWttX3Blcl9jYXJyb3QpKSsKICBnZW9tX3BvaW50KGFlcyhmaWxsPW5hbWUpLCBzaGFwZT0yMSwgc2l6ZT0zLCBzaG93LmxlZ2VuZCA9IEYpKwogIGdlb21fdGV4dChhZXMobGFiZWw9bmFtZSksIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeD0xMCwgeT0wLjQpKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cz1jKDAsMzAwKSkKYGBgCkRBWSAyMAoKT24gdGhlIHR3ZW50aWV0aCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi4gd2UgZGlkIHNvbWUgcG9zaXRpb24gYWRqdXN0bWVudHMgd2l0aCBiYXIgcGxvdHMhCgpGaXJzdCwgbGV0J3MgY3JlYXRlIGEgYmFycGxvdCBzaW5jZSB3ZSBoYXZlbid0IGRvbmUgdGhhdCB5ZXQuIFdlJ2xsIGJhc2UgaXQgb24gb3VyIHByZXZpb3VzIHNsZWlnaHMgc3Vic2V0LgpgYGB7cn0Kc2xlaWdocy5zdWJzZXQgJT4lCiAgZ2dwbG90KGFlcyh4PWJlbGxzLCB5PXJlaW5zKSkrCiAgZ2VvbV9jb2woKSsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cz1jKDQsIDYsIDgpKSsKICB0aGVtZV9jbGFzc2ljKCkKYGBgClRoZSBzbGVpZ2hzIGNvbWUgd2l0aCA0LCA2LCBvciA4IGJlbGxzLiBTbyBoZXJlIHdlJ3JlIGRpc3BsYXlpbmcgdGhlIGNvdW50cyBvZiBzbGVpZ2hzIGluIGVhY2ggY2F0ZWdvcnkgKCMgb2YgYmVsbHMpLiBCdXQgbWF5YmUgd2Ugd2FudCB0byBzZWUgc29tZSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGluIG91ciBiYXJwbG90LCBzdWNoIGFzIHRoZSBudW1iZXIgb2YgcmVpbnMgb24gdGhlIHNsZWlnaC4gSSd2ZSBoZWFyZCB0aGF0IFNhbnRhIHRha2VzIHRoZXNlIHRoaW5ncyBzdXBlciBzZXJpb3VzbHksIHNvIHRoaXMgaXMgY29tcGxldGVseSBwcmFjdGljYWwgYW5kIHJlYXNvbmFibGUgcGxvdC4gTm90ZSB0aGF0IHdlIGhhdmUgdG8gc3BlY2lmeSB0aGF0IHJlaW5zIGlzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUsIG5vdCBhIGNvbnRpbnVvdXMgb25lLCB1c2luZyBhcy5jaGFyYWN0ZXIoKS4gSW4gdGhpcyBjYXNlLCB3ZSBjYW4ndCBoYXZlIDMuNSByZWlucy4KYGBge3J9CnNsZWlnaHMuc3Vic2V0ICU+JQogIGdncGxvdChhZXMoeD1iZWxscywgeT1yZWlucywgZmlsbD1hcy5jaGFyYWN0ZXIocmVpbnMpKSkrCiAgZ2VvbV9jb2woKSsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cz1jKDQsIDYsIDgpKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPXhtYXMpCmBgYApUaGUgZGVmYXVsdCBpcyBhIHN0YWNrZWQgYmFycGxvdCAocG9zaXRpb24gPSAic3RhY2siKSwgYnV0IHRoZXJlIGFyZSBvdGhlciB3YXlzIHdlIGNvdWxkIGRpc3BsYXkgdGhpcyB1c2luZyBwb3NpdGlvbiBhZGp1c3RtZW50cy4gVGhpcyBvcHRpb24gc2hvd3MgaXQgYXMgYSBwZXJjZW50IHVzaW5nIHBvc2l0aW9uID0gImZpbGwiLgpgYGB7cn0Kc2xlaWdocy5zdWJzZXQgJT4lCiAgZ2dwbG90KGFlcyh4PWJlbGxzLCB5PXJlaW5zLCBmaWxsPWFzLmNoYXJhY3RlcihyZWlucykpKSsKICBnZW9tX2NvbChwb3NpdGlvbj0iZmlsbCIpKwogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzPWMoNCwgNiwgOCkpKwogIHRoZW1lX2NsYXNzaWMoKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9eG1hcykKYGBgCldlIGNhbiBhbHNvIHBvc2l0aW9uIHRoZW0gc2lkZSBieSBzaWRlIHVzaW5nIHBvc2l0aW9uID0gImRvZGdlIi4gTm90ZSB0aGF0IHRoZSByZWQgYmFyIG9uIHRoZSBsZWZ0IGFuZCB0aGUgZ3JlZW4gYmFyIG9uIHRoZSByaWdodCBhcmUgdHdvIGJhcnMgc2lkZSBieSBzaWRlLgpgYGB7cn0Kc2xlaWdocy5zdWJzZXQgJT4lCiAgZ2dwbG90KGFlcyh4PWJlbGxzLCB5PXJlaW5zLCBmaWxsPWFzLmNoYXJhY3RlcihyZWlucykpKSsKICBnZW9tX2NvbChwb3NpdGlvbj0iZG9kZ2UiKSsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cz1jKDQsIDYsIDgpKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPXhtYXMpCmBgYApXYW50IGEgcmV2aWV3PyBUcnkgY2hhbmdpbmcgdGhlIG5hbWUgb2YgeW91ciBsZWdlbmQuCgpEQVkgMjEKCk9uIHRoZSB0d2VudHktZmlyc3QgZGF5IG9mIENocmlzdG1hcy4uLgoKLi4ud2UgbGVhcm5lZCBhYm91dCBmYWNldGluZyEKCkZhY2V0aW5nIHByb2R1Y2VzIHNtYWxsZXIgZ3JhcGhzIHRoYXQgY2FuIGJlIGRpc3BsYXllZCBhbG9uZ3NpZGUgb25lIGFub3RoZXIuIFdlIHVzZSBmYWNldF93cmFwKCkgYW5kIGZhY2V0X2dyaWQoKSBmb3IgdGhpcy4gCgpMZXQncyBzdGFydCB3aXRoIGZhY2V0X3dyYXAoKS4gUmVtZW1iZXIgb3VyIGxpbmUgZ3JhcGggdGhhdCBsb29rcyBhIGJpdCBsaWtlIENocmlzdG1hcyBsaWdodHM/IExldCdzIHVzZSB0aGF0LiBIZXJlIGl0IGlzLCBhcyBhIHJlbWluZGVyOgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD1oZWlnaHQsIHk9eG1hcy5tYWdpYywgZ3JvdXA9dHlwZSkpKwogIGdlb21fbGluZShhZXMoY29sb3VyPXR5cGUpLCBsaW5ldHlwZT0iZGFzaGVkIikrCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyPXR5cGUpLCBzaXplID0gMywgc2hhcGUgPSA4KQpgYGAKTm93LCBpbnN0ZWFkIG9mIGhhdmluZyBhbGwgdGhyZWUgbGluZXMgb24gb25lIHBsb3QsIGxldCdzIGNyZWF0ZSB0aHJlZSBzbWFsbGVyIHBsb3RzIGFuZCBkaXNwbGF5IHRoZW0gdG9nZXRoZXIuIAoKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9aGVpZ2h0LCB5PXhtYXMubWFnaWMsIGdyb3VwPXR5cGUpKSsKICBnZW9tX2xpbmUobGluZXR5cGU9ImRhc2hlZCIpKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIHNoYXBlID0gOCkrCiAgZmFjZXRfd3JhcCh+dHlwZSwgbmNvbD0zKQpgYGAKTGV0J3MgZG8gdGhlIHNhbWUgdGhpbmcgYnV0IHVzaW5nIGZhY2V0X2dyaWQoKS4gVGhlIHN5bnRheCBpcyBhIGxpdHRsZSBkaWZmZXJlbnQsIGJ1dCB3ZSd2ZSBwcm9kdWNlZCB0aGUgZXhhY3Qgc2FtZSBzZXQgb2YgcGxvdHMuIEluIG91ciBjYXNlLCAiLn50eXBlIiBwdXRzIHRoZSBwbG90cyBzaWRlIGJ5IHNpZGUuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiBnZW9tX2xpbmUobGluZXR5cGU9ImRhc2hlZCIpKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIHNoYXBlID0gOCkrCiAgZmFjZXRfZ3JpZCgufnR5cGUpCmBgYApJZiB3ZSB3YW50IHRvIHN0YWNrIG91ciBwbG90cyBpbnN0ZWFkLCB3ZSBjaGFuZ2UgdXAgdGhlIGNvZGluZyB3aXRoaW4gZmFjZXRfZ3JpZCgpLiBJbiBvdXIgY2FzZSwgInR5cGV+LiIgc3RhY2tzIHRoZSBwbG90cy4KYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9aGVpZ2h0LCB5PXhtYXMubWFnaWMsIGdyb3VwPXR5cGUpKSsKIGdlb21fbGluZShsaW5ldHlwZT0iZGFzaGVkIikrCiAgZ2VvbV9wb2ludChzaXplID0gMywgc2hhcGUgPSA4KSsKICBmYWNldF9ncmlkKHR5cGV+LikKYGBgCk91ciBkYXRhc2V0cyBhcmVuJ3QgcmVhbGx5IHNldCB1cCBmb3IgdGhpcyB0eXBlIG9mIGdyaWQsIGJ1dCBsZXQncyBsb29rIGF0IHBsb3RzIG9mIHJlaW5zIGJ5IGJlbGxzIHRvIHNob3cgeW91IGhvdyB5b3UgY291bGQgc2V0IHVwIGZhY2V0X2dyaWQoKSB3aXRoIG11bHRpcGxlIHBsb3RzLiBBIHBsb3QgYXJlYSBpcyBwcm9kdWNlZCB3aXRoIHR3byBsZXZlbHMgZm9yIHJlaW5zIGFuZCB0aHJlZSBsZXZlbHMgZm9yIGJlbGxzLgpgYGB7cn0Kc2xlaWdocy5zdWJzZXQlPiUKICBnZ3Bsb3QoYWVzKHg9cmVpbnMsIHk9YmVsbHMpKSsKICBmYWNldF9ncmlkKHJlaW5zfmJlbGxzKQpgYGAKWW91IGNhbiBhbHNvIHVzZSAic2NhbGVzID0iIHRvIGFkanVzdCB0aGUgc2NhbGVzIG9mIGFsbCBvciBlYWNoIG9mIHRoZSBwbG90cy4gTGV0J3MgZ28gYmFjayB0byBvdXIgZmlyc3Qgc2V0IG9mIHBsb3RzIGZyb20gdG9kYXk6CmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiAgZ2VvbV9saW5lKGxpbmV0eXBlPSJkYXNoZWQiKSsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBzaGFwZSA9IDgpKwogIGZhY2V0X3dyYXAofnR5cGUsIG5jb2w9MykKYGBgClRvIGFkanVzdCB0aGUgc2NhbGVzLCB3ZSBhZGQgInNjYWxlcyA9IiB0byBmYWNldF93cmFwKCkuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiAgZ2VvbV9saW5lKGxpbmV0eXBlPSJkYXNoZWQiKSsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBzaGFwZSA9IDgpKwogIGZhY2V0X3dyYXAofnR5cGUsIG5jb2w9Mywgc2NhbGVzID0gImZyZWVfeSIpCmBgYApCeSBjaG9vc2luZyAiZnJlZV95IiB3ZSBoYXZlIGNoYW5nZWQgdGhlIHkgc2NhbGUgZnJvbSBmaXhlZCB0byBmcmVlIGJ1dCB0aGUgeC1heGlzIHJlbWFpbmVkIHRoZSBzYW1lLiBUYWtlIGEgbG9vayBhdCBob3cgdGhlIHktYXhpcyBzY2FsZSBpcyBub3cgZGlmZmVyZW50IGZvciBlYWNoIHBsb3QuIE90aGVyIG9wdGlvbnMgYXJlICJmcmVlX3giLCAiZml4ZWQiLCBvciAiZnJlZSIuCgpUaGVyZSBhcmUgbG90cyBtb3JlIHRoaW5ncyB5b3UgY2FuIGRvIHdpdGggZmFjZXRzLiBDaGVjayBvdXQgQ2guIDE3IG9mICJnZ3Bsb3QyOkVsZWdhbnQgR3JhcGhpY3MgZm9yIERhdGEgQW5hbHlzaXMiIGZvciBtb3JlOiBodHRwczovL2dncGxvdDItYm9vay5vcmcvZmFjZXQuaHRtbAoKV2FudCBhIHJldmlldz8gVHJ5IGNoYW5naW5nIHRoZSBzdHlsZSBvZiB0aGUgcG9pbnRzIGFuZCBsaW5lcyBpbiB0aGVzZSBwbG90cy4gQWxzbywgdHJ5IHJlbW92aW5nIHRoZSBncmV5IGJhY2tncm91bmQhCgpEQVkgMjIKCk9uIHRoZSB0d2VudHktc2Vjb25kIGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLndlIGFubm90YXRlZCBvdXIgcGxvdHMhCgpTb21ldGltZXMgd2UgbWF5IHdhbnQgdG8gYWRkIHRleHQgdG8gb3VyIHBsb3RzLCBub3QganVzdCB0aXRsZXMgYW5kIGxhYmVscywgYnV0IGFubm90YXRpb25zIG9uIHRoZSBkYXRhIG9yIHBsb3QgYXJlYS4gV2UgY2FuIGRvIHRoaXMgdXNpbmcgZ2VvbV90ZXh0KCkuIFdlIGRpZCB0aGlzIG9uIERheSAxOSB3aGVuIHdlIHRhbGtlZCBhYm91dCBwb3NpdGlvbiBhZGp1c3RtZW50cy4gTm93IHdlJ3JlIGdvaW5nIHRvIGRpc2N1c3MgYW5ub3RhdGlvbnMgaW4gbW9yZSBkZXRhaWwuIExldCdzIGJyaW5nIGJhY2sgdGhhdCBncmFwaGljLCBidXQgd2l0aCBzb21lIGFkZGVkIHRleHQgYW5kIGxhYmVscy4KYGBge3J9CnNsZWlnaHMuc3Vic2V0ICU+JQogIGdncGxvdChhZXMoeD1kZWVycG93ZXIsIHk9a21fcGVyX2NhcnJvdCkpKwogIGdlb21fcG9pbnQoYWVzKGZpbGw9bmFtZSksIHNoYXBlPTIxLCBzaXplPTMsIHNob3cubGVnZW5kID0gRikrCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1uYW1lKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4PTEwLCB5PTAuNCkpKwogIHRoZW1lX2NsYXNzaWMoKSsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzPWMoMCwzMDApKSsKICBsYWJzKHg9IkRlZXJwb3dlciIsIHk9IktpbG9tZXRlcnMgcGVyIGNhcnJvdCIsIHRpdGxlPSJUb3Agb3B0aW9ucyBmb3IgU2FudGEncyBzbGVpZ2ggdXBncmFkZSIsIHN1YnRpdGxlPSAiU291cmNlOiBUaGUgVWx0aW1hdGUgU2xlaWdoIENhdGFsb2d1ZSAyMDIyIikKYGBgCkxldCdzIHNheSB0aGF0IHRoZSBlbGYgaW4gY2hhcmdlIHdhbnRzIHRvIHNlbmQgdGhpcyB0byBTYW50YSBidXQgd2FudHMgdG8gbWFyaywgb24gdGhlIHBsb3QsIHdoaWNoIHNsZWlnaCBpcyBoZXIgdG9wIGNob2ljZSBmb3IgU2FudGEuIFdlIGNhbiBkbyB0aGlzIHVzaW5nIHRoZSBhbm5vdGF0ZSgpIGZ1bmN0aW9uLiBXZSBmaXJzdCBuZWVkIHRvIHNldCB1cCBvdXIgeCAmIHkgcmFuZ2VzIGFuZCBvdXIgY2FwdGlvbiB0ZXh0LgoKYGBge3J9CnlybmcgPC0gcmFuZ2Uoc2xlaWdocy5zdWJzZXQka21fcGVyX2NhcnJvdCkKeHJuZyA8LSByYW5nZShzbGVpZ2hzLnN1YnNldCRkZWVycG93ZXIpCmNhcHRpb24gPC0gcGFzdGUoc3Ryd3JhcCgiU3ByaW5rbGUncyB0b3AgY2hvaWNlOiBXaW50ZXIgRXhwcmVzcyIpKQpgYGAKClRoZW4gd2UgY2FuIG1ha2Ugb3VyIHBsb3Q6CmBgYHtyfQpzbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGVlcnBvd2VyLCB5PWttX3Blcl9jYXJyb3QpKSsKICBnZW9tX3BvaW50KGFlcyhmaWxsPW5hbWUpLCBzaGFwZT0yMSwgc2l6ZT0zLCBzaG93LmxlZ2VuZCA9IEYpKwogIGdlb21fdGV4dChhZXMobGFiZWw9bmFtZSksIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeD0xMCwgeT0wLjQpKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cz1jKDAsMzAwKSkrCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgeCA9IHhybmdbMV0sIHkgPSB5cm5nWzJdLCAKICAgIGxhYmVsID0gY2FwdGlvbiwgaGp1c3QgPSAtMC40LCB2anVzdCA9IDEsIHNpemUgPSA1LCBmb250ZmFjZT0iYm9sZCIpCmBgYAoKT3IgbWF5IHdlIHdhbnQgdG8gYW5ub3RhdGUgdGhlIHBvaW50cyBkaXJlY3RseSB0byBzaG93IFNhbnRhIHdoaWNoIG9uZXMgU3ByaW5rbGUgY2hvc2UgYXMgaGVyIHRvcCByZWNvbW1lbmRhdGlvbnMuCgpgYGB7cn0Kc2xlaWdocy5zdWJzZXQgJT4lCiAgZ2dwbG90KGFlcyh4PWRlZXJwb3dlciwgeT1rbV9wZXJfY2Fycm90KSkrCiAgZ2VvbV9wb2ludChhZXMoZmlsbD1uYW1lKSwgc2hhcGU9MjEsIHNpemU9Mywgc2hvdy5sZWdlbmQgPSBGKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPW5hbWUpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHg9MTAsIHk9MC40KSkrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHM9YygwLDMwMCkpKwogIGFubm90YXRlKCJ0ZXh0IiwgeD1jKDU1KSwgeT1jKDIxLjYpLCBsYWJlbD1jKCJUb3AgY2hvaWNlISIpLCBzaXplID0gNywgZm9udGZhY2U9ImJvbGQiKQpgYGAKCkhvcGVmdWxseSBpdCdzIGNsZWFyIHdoaWNoIHBvaW50IHdlJ3JlIHJlZmVycmluZyB0byBoZXJlICgiV2ludGVyIEV4cHJlc3MiKSBidXQgaW4gY2FzZSBpdCdzIG5vdCwgd2UgY2FuIGFsc28gaGlnaGxpZ2h0IGl0IGJ5IGFkZGluZyBhbm90aGVyIGdlb21fcG9pbnQoKSBsYXllci4gTm90ZSB0aGF0IHdlIGFkZCBhZGRpdGlvbmFsIGdlb21fcG9pbnQoKSBsYXllcnMgYnV0IHRoZXkgbXVzdCBnbyBiZWZvcmUgb3VyIG9yaWdpbmFsIGdlb21fcG9pbnQgbGF5ZXIgb3IgdGhlIG9yYW5nZSBkb3RzIHdpbGwgYXBwZWFyIG9uIHRvcCBvZiB0aGUgY29sb3VyZWQgcG9pbnRzICh3aGljaCBpbiB0aGlzIGNhc2Ugd291bGQgYWxzbyBiZSBmaW5lISkuCmBgYHtyfQpzbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGVlcnBvd2VyLCB5PWttX3Blcl9jYXJyb3QpKSsKICAgZ2VvbV9wb2ludChkYXRhPWZpbHRlcihzbGVpZ2hzLnN1YnNldCwgbmFtZSA9PSJXaW50ZXIgRXhwcmVzcyIpLCBjb2xvdXI9Im9yYW5nZSIsIHNpemU9NikrCiAgZ2VvbV9wb2ludChhZXMoZmlsbD1uYW1lKSwgc2hhcGU9MjEsIHNpemU9Mywgc2hvdy5sZWdlbmQgPSBGKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPW5hbWUpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHg9MTAsIHk9MC40KSkrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHM9YygwLDMwMCkpKwphbm5vdGF0ZSgidGV4dCIsIHg9Yyg1NSksIHk9YygyMS42KSwgbGFiZWw9YygiVG9wIGNob2ljZSEiKSwgc2l6ZSA9IDcsIGZvbnRmYWNlPSJib2xkIikKYGBgClRoZXJlJ3MgbXVjaCBtb3JlIHlvdSBjYW4gZG8gd2l0aCBhbm5vdGF0aW9ucy4gQXMgdXN1YWwsIEknbGwgZGlyZWN0IHlvdSB0byBnZ3Bsb3QyOiBFbGVnYW50IEdyYXBoaWNzIGZvciBEYXRhIEFuYWx5c2lzOiBodHRwczovL2dncGxvdDItYm9vay5vcmcvYW5ub3RhdGlvbnMuaHRtbCNkaXJlY3QtbGFiZWxsaW5nCgpXYW50IGEgcmV2aWV3PyBUcnkgY2hhbmdpbmcgdGhlIGNvbG91ciBwYWxldHRlIG9mIHRoZSBwb2ludHMgaW4gdGhpcyBwbG90LgoKREFZIDIzCgpPbiB0aGUgdHdlbnR5LXRoaXJkIGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLndlIGludHJvZHVjZWQgY293cGxvdCEhCgoiQ293cGxvdD8/Pz8/Pz8/OgoKWWVzLiBDb3dwbG90LgoKQ293cGxvdCBpcyBhbiBhZGQtb24gdG8gZ2dwbG90IGFuZCBhbGxvd3MgdXMgdG8gY29tYmluZSBzZXZlcmFsIHBsb3RzIGludG8gb25lLgoKIkhvdyBpcyB0aGF0IGRpZmZlcmVudCBmcm9tIGZhY2V0aW5nPz8iCgpDb3dwbG90IGFsbG93cyB5b3UgdG8gY29tYmluZSBwbG90cyBvZiBkaWZmZXJlbnQgdHlwZXMgaW50byBvbmUgaW1hZ2UhCgpGaXJzdCwgbGV0J3MgaW5zdGFsbCBhbmQgbG9hZCB0aGUgY293cGxvdCBwYWNrYWdlLgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygiY293cGxvdCIpCmxpYnJhcnkoY293cGxvdCkKYGBgCgpOb3cgbGV0J3MgbWFrZSBhIGZldyBncmFwaHMuCmBgYHtyfQpzbGVpZ2gucGxvdDEgPC1zbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9a21fcGVyX2NhcnJvdCwgeT1iYWdfc3BhY2UpKSsKICBnZW9tX3BvaW50KGNvbG91cj0iZGFya2dyZWVuIikrCiAgdGhlbWVfY2xhc3NpYygpCgpzbGVpZ2gucGxvdDIgPC1zbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9YmVsbHMpKSsKICBnZW9tX2JhcihmaWxsPSJmaXJlYnJpY2syIikrCiAgdGhlbWVfY2xhc3NpYygpCgpzbGVpZ2gucGxvdDMgPC1zbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9a21fcGVyX2NhcnJvdCwgeT1kZWVycG93ZXIpKSsKICBnZW9tX3F1YW50aWxlKGNvbG91cj0iZ29sZDMiKSsKICB0aGVtZV9jbGFzc2ljKCkKCnRyZWUucGxvdDE8LXRyZWVzICU+JQogIGdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fYm94cGxvdChmaWxsPSJzZWFncmVlbiIpKwogIHRoZW1lX2NsYXNzaWMoKQoKdHJlZS5wbG90MiA8LXRyZWVzICU+JQogIGdncGxvdChhZXMoeD14bWFzLm1hZ2ljKSkrCiAgZ2VvbV9kb3RwbG90KGZpbGw9InRvbWF0bzMiKSsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCk5vdyB3ZSBjYW4gdXNlIGNvd3Bsb3QgdG8gY3JlYXRlIG9uZSBpbWFnZSB3aXRoIG11bHRpcGxlIHBsb3RzLgpgYGB7cn0KcGxvdF9ncmlkKHRyZWUucGxvdDEsIHRyZWUucGxvdDIsIG5yb3c9MSwgbGFiZWxzID0gYygiQSIsICJCIikpCmBgYApMZXQncyBjb21iaW5lIGFsbCA1IHBsb3RzIGludG8gb25lIGltYWdlLgpgYGB7cn0KcGxvdF9ncmlkKHRyZWUucGxvdDEsIHRyZWUucGxvdDIsIHNsZWlnaC5wbG90MSwgc2xlaWdoLnBsb3QyLCBzbGVpZ2gucGxvdDMsIG5yb3cgPSAyKQpgYGAKVGhpcyBsb29rcyBva2F5LCBidXQgbWF5YmUgd2Ugd2FudGVkIHRoZSB0cmVlIHBsb3RzIHRvIGJlIG9uIHRoZSB0b3AgYW5kIHRoZSBzbGVpZ2ggcGxvdHMgdG8gYmUgb24gdGhlIGJvdHRvbS4gV2UgY2FuIGRvIHRoYXQgYnV0IHNwZWNpZnlpbmcgd2hpY2ggb25lcyBnbyBpbiB0aGUgdG9wIHJvdyBhbmQgd2hpY2ggb24gdGhlIGJvdHRvbS4KCmBgYHtyfQpmaXJzdF9yb3cgPC1wbG90X2dyaWQocGxvdF9ncmlkKHRyZWUucGxvdDEsIHRyZWUucGxvdDIsIG5yb3c9MSwgbGFiZWxzID0gYygiQSIsICJCIikpKQpzZWNvbmRfcm93IDwtcGxvdF9ncmlkKHNsZWlnaC5wbG90MSwgc2xlaWdoLnBsb3QyLCBzbGVpZ2gucGxvdDMsIG5yb3c9MSwgbGFiZWxzID0gYygiQSIsICJCIiwgIkMiKSkKCnBsb3RfZ3JpZChmaXJzdF9yb3csIHNlY29uZF9yb3csIG5jb2w9MSwgbnJvdz0yKQpgYGAKV2FudCBhIHJldmlldz8gVHJ5IGNoYW5naW5nIHlvdXIgYXhpcyBsYWJlbHMgdG8gc29tZXRoaW5nIGNsZWFuZXIgYW5kIG1vcmUgaW5mb3JtYXRpdmUgKHByZXRlbmQgeW91IHdlcmUgZ29pbmcgdG8gcHVibGlzaCB0aGlzIGltYWdlIGluIGEgcGFwZXIhKS4gSGludDogeW91IHdpbGwgbmVlZCB0byBlZGl0IHRoZSBsYWJlbHMgaW4geW91ciBvcmlnaW5hbCBwbG90cywgbm90IGluIHRoZSBwbG90X2dyaWQoKS4gSWYgeW91IGNhbid0IHJlbWVtYmVyIGhvdywgY2hlY2sgb3V0IGRheSAxMC4KCkRBWSAyNAoKT24gdGhlIHR3ZW50eS1mb3VydGggZGF5IG9mIENocmlzdG1hcy4uLgoKLi4ud2UgcHV0IG91ciBrbm93bGVkZ2UgdG8gdGhlIHRlc3QhCgpUb2RheSB3ZSB3aWxsIG5vdCBsZWFybiBhbnl0aGluZyBuZXcuIEluc3RlYWQsIHdlIHdpbGwgYnJpbmcgdG9nZXRoZXIgd2hhdCB3ZSd2ZSBsZWFybmVkIHRvIGJ1aWxkIHNvbWUgcGxvdHMhCgpXZSB3aWxsIGJ1aWxkIHRocmVlIGRpZmZlcmVudCBwbG90czoKCigxKSBCdWlsZCBhIHBsb3QgdXNpbmcgdGhlIHRyZWVzIGRhdGFzZXQgdGhhdCBzaG93cyB2aW9saW4gcGxvdHMgYnkgdHJlZSB0eXBlLCBhbHNvIGNvbG91cmVkIGJ5IHRyZWUgdHlwZS4gQWRkIGEgdGl0bGUsIGluZm9ybWF0aXZlIGF4aXMgbGFiZWxzLCBhbmQgYSBsZWdlbmQgYXQgdGhlIGJvdHRvbS4KCigyKSBCdWlsZCBhIHBsb3QgdXNpbmcgdGhlIHNsZWlnaHMgZGF0YXNldCB0aGF0IHNob3dzIGRlZXJwb3dlciBieSB3ZWlnaHQsIHdpdGggYSB0cmVuZGxpbmUsIHBvaW50cyBjb2xvdXJlZCBieSBrbSBwZXIgY2Fycm90ICh3aXRoIGEgc2l6ZSBhbmQgc2hhcGUgd2hlcmUgeW91IGNhbiBzZWUgdGhlIGNvbG91ciBkaWZmZXJlbmNlcyksIGFuZCBhbiBpbmZvcm1hdGl2ZSB0aXRsZSwgYXhpcyBsYWJlbHMsIGFuZCBsZWdlbmQuCgooMykgQnVpbGQgYW4gaW1hZ2UgdGhhdCBzaG93cyB0aGUgcHJldmlvdXMgdHdvIGdyYXBocyBzaWRlLWJ5LXNpZGUgaW4gb25lIGltYWdlIHdpdGggbGFiZWxzIEEgYW5kIEIuCgpBbmQgYXMgYSBsaXR0bGUgQ2hyaXN0bWFzIEV2ZSBnaWZ0LCBoZXJlIGlzIGEgZmFudGFzdGljIGNoZWF0c2hlZXQgZm9yIGdncGxvdDIuIEkga2VlcCBpdCBpbiBteSBib29rbWFya3MgYmFyIDopCmh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL2Jsb2IvbWFpbi9kYXRhLXZpc3VhbGl6YXRpb24tMi4xLnBkZiAtIHRoaXMgbWlnaHQgY29tZSBpbiBoYW5keSB3aGlsZSB5b3UgYXJlIHdvcmtpbmcgdGhyb3VnaCB0aGVzZSBleGVyY2lzZXMhCgpEQVkgMjUgLSBNRVJSWSBDSFJJU1RNQVMhISEKCkkgaG9wZSB5b3UgZW5qb3llZCB0aGlzIGFkdmVudCBjYWxlbmRhci4gU2ltaWxhcmx5IHRvIHRoZSBwcmV2aW91cyBvbmUsIGZvciBkYXkgMjUsIEkndmUgZ2l2ZW4geW91IGNvZGUgZm9yIGEgQ2hyaXN0bWFzIHZpc3VhbCBjcmVhdGVkIGJ5IHNvbWVvbmUgZWxzZSAoaW4gdGhpcyBjYXNlLCBkYXRhIHNjaWVudGlzdCwgSm9kaWUgQnVyY2hlbGwpLiBCdXQgdW5saWtlIGluIHRoZSBvcmlnaW5hbCBSIGFkdmVudCBjYWxlbmRhUiwgbm93IHlvdSBzaG91bGQgYmUgYWJsZSB0byB1bmRlcnN0YW5kIGEgbG90IG9mIHRoZSBjb21wb25lbnRzIGFuZCB0aGUgZ3JhbW1hciB1c2VkIGluIHRoaXMgY29kZS4gTG9hZCB0aGUgZGF0YSBhbmQgcnVuIHRoZSBjb2RlIHRvIHNlZSB3aGF0IGhhcHBlbnMhIFRoZSBvcmlnaW5hbCBibG9nIHBvc3QgY2FuIGJlIGZvdW5kIGhlcmU6IGh0dHBzOi8vdC1yZWRhY3R5bC5pby9ibG9nLzIwMTYvMTIvYS12ZXJ5LWdncGxvdDItY2hyaXN0bWFzLmh0bWwKCkZpcnN0LCBsb2FkIGluIHRoaXMgZGF0YXNldCwgd2hpY2ggaXMgYXZhaWxhYmxlIHRocm91Z2ggZ2l0IGh1Yi4KYGBge3J9CkNocmlzdG1hc1RyZWUgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS90LXJlZGFjdHlsL0Jsb2ctcG9zdHMvbWFzdGVyL0NocmlzdG1hcyUyMHRyZWUlMjBiYXNlJTIwZGF0YS5jc3YiKQpgYGAKCmBgYHtyfQojIEdlbmVyYXRlIHRoZSAibGlnaHRzIgpEZXNpcmVkLkxpZ2h0cyA8LSA1MApUb3RhbC5MaWdodHMgPC0gc3VtKHJvdW5kKERlc2lyZWQuTGlnaHRzICogMC4zNSkgKyByb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMjApICsgCiAgICAgICAgICAgICAgICAgICAgICByb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTcpICsgcm91bmQoRGVzaXJlZC5MaWdodHMgKiAwLjEzKSArCiAgICAgICAgICAgICAgICAgICAgICByb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTApICsgcm91bmQoRGVzaXJlZC5MaWdodHMgKiAwLjA1KSkKCkxpZ2h0cyA8LSBkYXRhLmZyYW1lKExpZ2h0cy5YID0gYyhyb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMzUpLCA0LCAxOCksIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMjApLCA1LCAxNyksIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTcpLCA2LCAxNiksIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTMpLCA3LCAxNSksIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTApLCA4LCAxNCksIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMDUpLCAxMCwgMTIpLCAwKSkpCkxpZ2h0cyRMaWdodHMuWSA8LSBjKHJvdW5kKHJ1bmlmKHJvdW5kKERlc2lyZWQuTGlnaHRzICogMC4zNSksIDQsIDYpLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMjApLCA3LCA4KSwgMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQocnVuaWYocm91bmQoRGVzaXJlZC5MaWdodHMgKiAwLjE3KSwgOSwgMTApLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTMpLCAxMSwgMTIpLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTApLCAxMywgMTQpLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMDUpLCAxNSwgMTcpLCAwKSkKTGlnaHRzJExpZ2h0cy5Db2xvdXIgPC0gYyhyb3VuZChydW5pZihUb3RhbC5MaWdodHMsIDEsIDQpLCAwKSkKCiMgR2VuZXJhdGUgdGhlICJiYXVibGVzIgpCYXVibGVzIDwtIGRhdGEuZnJhbWUoQmF1YmxlLlggPSBjKDYsIDksIDE1LCAxNywgNSwgMTMsIDE2LCA3LCAxMCwgMTQsIDcsIDksIDExLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxNCwgOCwgMTQsIDksIDEyLCAxMSwgMTIsIDE0LCAxMSwgMTcsIDEwKSkKQmF1YmxlcyRCYXVibGUuWSA8LSBjKDQsIDUsIDQsIDQsIDUsIDUsIDUsIDYsIDYsIDYsIDgsIDgsIDgsIDgsIDEwLAogICAgICAgICAgICAgICAgICAgICAgMTAsIDExLCAxMSwgMTIsIDEzLCAxMCwgMTYsIDcsIDE0KQpCYXVibGVzJEJhdWJsZS5Db2xvdXIgPC0gZmFjdG9yKGMoMSwgMiwgMiwgMywgMiwgMywgMSwgMywgMSwgMSwgMSwgMiwgMSwgMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDMsIDMsIDIsIDEsIDMsIDIsIDEsIDMsIDMsIDEpKQpCYXVibGVzJEJhdWJsZS5TaXplIDwtIGMoMSwgMywgMSwgMSwgMiwgMSwgMiwgMiwgMiwgMSwgMSwgMSwgMywgMywgMywKICAgICAgICAgICAgICAgICAgICAgICAgIDIsIDMsIDEsIDEsIDIsIDIsIDMsIDMsIDIpCgojIEdlbmVyYXRlIHRoZSBwbG90CmdncGxvdCgpICsgCiAgZ2VvbV90aWxlKGRhdGEgPSBDaHJpc3RtYXNUcmVlLCBhZXMoeCA9IFRyZWUuWCwgeSA9IFRyZWUuWSwgZmlsbCA9IFRyZWUuQ29sb3VyKSkgKwogIHNjYWxlX2ZpbGxfaWRlbnRpdHkoKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IExpZ2h0cywgYWVzKHggPSBMaWdodHMuWCwgeSA9IExpZ2h0cy5ZLCBhbHBoYSA9IExpZ2h0cy5Db2xvdXIpLAogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z29sZGVucm9keWVsbG93Iiwgc2hhcGUgPSAxNikgKwogIGdlb21fcG9pbnQoZGF0YSA9IEJhdWJsZXMsIGFlcyh4ID0gQmF1YmxlLlgsIHkgPSBCYXVibGUuWSwgY29sb3VyID0gQmF1YmxlLkNvbG91ciwgc2l6ZSA9IEJhdWJsZS5TaXplKSwKICAgICAgICAgICAgIHNoYXBlID0gMTYpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoImZpcmVicmljazIiLCAiZ29sZCIsICJkb2RnZXJibHVlMyIpKSArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gMTIpICsKICB0aGVtZV9idygpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDIuNSwgeGVuZCA9IDQuNSwgeSA9IDEuNSwgeWVuZCA9IDEuNSksIGNvbG91ciA9ICJibHVldmlvbGV0Iiwgc2l6ZSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSA1LjUsIHhlbmQgPSA4LjUsIHkgPSAxLjUsIHllbmQgPSAxLjUpLCBjb2xvdXIgPSAiZG9kZ2VyYmx1ZTMiLCBzaXplID0gMikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDEzLjUsIHhlbmQgPSAxNi41LCB5ID0gMS41LCB5ZW5kID0gMS41KSwgY29sb3VyID0gImJsdWV2aW9sZXQiLCBzaXplID0gMikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDE3LjUsIHhlbmQgPSAxOS41LCB5ID0gMS41LCB5ZW5kID0gMS41KSwgY29sb3VyID0gImRvZGdlcmJsdWUzIiwgc2l6ZSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAzLjUsIHhlbmQgPSAzLjUsIHkgPSAwLjUsIHllbmQgPSAyLjUpLCBjb2xvdXIgPSAiYmx1ZXZpb2xldCIsIHNpemUgPSAyKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gNy4wLCB4ZW5kID0gNy4wLCB5ID0gMC41LCB5ZW5kID0gMi41KSwgY29sb3VyID0gImRvZGdlcmJsdWUzIiwgc2l6ZSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAxNS4wLCB4ZW5kID0gMTUuMCwgeSA9IDAuNSwgeWVuZCA9IDIuNSksIGNvbG91ciA9ICJibHVldmlvbGV0Iiwgc2l6ZSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAxOC41LCB4ZW5kID0gMTguNSwgeSA9IDAuNSwgeWVuZCA9IDIuNSksIGNvbG91ciA9ICJkb2RnZXJibHVlMyIsIHNpemUgPSAyKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMTEsIHkgPSAyMCwgbGFiZWwgPSAiTWVycnkgQ2hyaXN0bWFzISIsCiAgICAgICAgICAgc2l6ZSA9IDEyKSArCiAgbGFicyh4ID0gIiIsIHkgPSAiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCg==